Goal: the smallest possible bootable Linux 7.1 kernel.
Result: a 945 KB bzImage (967 680 bytes) that boots to userspace in
~0.29 s under QEMU/KVM and runs a PID 1 init from an initramfs.
| Metric | Value |
|---|---|
| Kernel | Linux 7.1.0 |
bzImage size |
945 KB (967 680 bytes) |
Enabled (=y) config symbols |
522 (vs. ~1850 for the modern build) |
| Build time | ~40 s on 12 cores |
| Time-to-userspace | 0.294 s (KVM) |
| initramfs | 308 KB (static init + /dev/console) |
make tinyconfig produces the absolute minimum config the kernel will accept —
on x86 that is a 32-bit, console-less kernel (367 =y symbols) that cannot
reach userspace. To make it bootable we add only what is strictly required:
| Option | Why it's needed |
|---|---|
CONFIG_64BIT |
build a 64-bit kernel (run a 64-bit init) |
CONFIG_PRINTK |
emit kernel log messages at all |
CONFIG_TTY + CONFIG_SERIAL_8250(_CONSOLE) |
a console on the QEMU serial port |
CONFIG_BINFMT_ELF |
execute an ELF init binary |
CONFIG_BLK_DEV_INITRD |
unpack and run an initramfs |
CONFIG_EARLY_PRINTK |
see messages before the console is fully up |
That is the whole philosophy of "minimal": start from nothing, add only the handful of symbols that take you from "won't boot" to "runs userspace". The result still has no networking, no block devices, no filesystems beyond the in-memory initramfs, no modules — by design.
KSRC=~/os_experiments/linux_kernel/src
BMIN=~/os_experiments/linux_kernel/build-minimal
# 1. absolute-minimum baseline (out-of-tree build dir via O=)
make -C "$KSRC" O="$BMIN" tinyconfig
# 2. add only the options needed to boot + run an init
"$KSRC"/scripts/config --file "$BMIN/.config" \
-e 64BIT -e PRINTK -e PRINTK_TIME -e TTY \
-e SERIAL_8250 -e SERIAL_8250_CONSOLE \
-e BINFMT_ELF -e BLK_DEV_INITRD -e EARLY_PRINTK
make -C "$KSRC" O="$BMIN" olddefconfig # resolve dependencies
# 3. build (≈40 s)
make -C "$KSRC" O="$BMIN" -j"$(nproc)" bzImage
# -> $BMIN/arch/x86/boot/bzImage (945 KB)A kernel with no root filesystem panics with "No init found". We give it the
tiniest possible userspace: a single static init that prints a banner and
powers off. Device nodes are added without root using the kernel's own
gen_init_cpio tool (so /dev/console exists and the init's stdout reaches the
serial port):
gcc -static -Os -s -o init init.c # statically linked PID 1
gcc -O2 -o gen_init_cpio "$KSRC/usr/gen_init_cpio.c"
cat > initramfs.list <<EOF
dir /dev 0755 0 0
nod /dev/console 0600 0 0 c 5 1
file /init $PWD/init 0755 0 0
EOF
./gen_init_cpio initramfs.list | gzip -9 > initramfs.cpio.gz # 308 KBinit.c is ~20 lines: write(1, banner), then reboot(RB_POWER_OFF).
qemu-system-x86_64 -M pc -accel kvm -m 256M \
-kernel arch/x86/boot/bzImage \
-initrd initramfs.cpio.gz \
-append "console=ttyS0" \
-nographic -no-rebootCaptured output (artifacts/minimal/boot.log), trimmed:
[ 0.261603] Unpacking initramfs...
[ 0.294286] Run /init as init process
==================================================
Hello from the MINIMAL Linux 7.1 kernel!
I am PID 1 (init), launched from initramfs.
The whole bzImage is < 1 MB.
==================================================
[ 0.696909] reboot: Power off not available: System halted instead
The kernel reaches userspace in 0.29 s and our init runs. The
"Power off not available" line is expected and is itself a sign of how minimal
the kernel is — it has no ACPI, so it cannot power the machine off; it halts and
QEMU is reaped by the wrapper's timeout. (Adding ACPI for a clean power-off would
mean leaving "minimal" territory.)
WSL2 note:
/dev/kvmexists but is group-restricted. We enabled acceleration withsudo usermod -aG kvm $USER+sudo chmod 0666 /dev/kvm. Without KVM, QEMU still boots this kernel under pure software emulation (TCG), just much slower — fine for a one-off, painful for iteration.
Everything is captured under artifacts/minimal/:
bzImage, config, initramfs.cpio.gz, init.c, boot.log, and
run-qemu.sh (one-command re-boot).