Skip to content

Instantly share code, notes, and snippets.

@zouppen
Last active October 2, 2024 11:12
Show Gist options
  • Save zouppen/819fc0fe9fffb709d3d45db21527fa64 to your computer and use it in GitHub Desktop.
Save zouppen/819fc0fe9fffb709d3d45db21527fa64 to your computer and use it in GitHub Desktop.
How to setup a minimal diskless Debian initramfs with debootstrap for QEMU or iPXE

How to setup a minimal diskless Debian (year 2022 style)

This guide helps you to setup a minimal stateless and diskless Debian initramfs with debootstrap. It is suitable for running from QEMU or network booting with (e.g. with iPXE).

This guide is for amd64 architecture.

Motivation

In my case I needed to run MikroTik EoIP driver on my system which is a bit sketchy but works. I didn't want to mix it up with my system since upgrading the kernel leads to recompiling because the driver is not building automatically with DKMS and it with certain commands I was able to crash the whole system. I could debug it but since I'm going to migrate away from using EoIP anyway in a year, it's not worth fixing.

This could be useful for folks wanting to run some VPN client which is too sketchy or suspicious to run on the host system.

Why not Docker

Docker shares the same kernel with the host system which is good for performance, but it's not possible to load your own kernel modules or have a completely different version of it running.

Setting up the base system

First, we use debootstrap on pre-existing system (such as the host) to set up a minimal base system. You can use any directory, only restriction is file system mount options. Root is mounted without nodev so it's working for sure.

mkdir /stable-chroot
debootstrap stable /stable-chroot http://deb.debian.org/debian/

Now, you can configure the system by chrooting to it:

chroot /stable-chroot /bin/bash

It is a good practice to mount /sys, /proc and copy some basic files from the host system if you are doing something special. See Debian wiki page for an example.

Install kernel

This is important part. Pick you a kernel of your choice. I recommend cloud kernels if you are going to run it inside QEMU or not planning to use it on a special hardware. On chroot you can run:

apt install linux-image-YOUR_VERSION_HERE-cloud-amd64-unsigned

Creating initramfs

Installing a kernel creates an initrd file, but we are not going to use it. Instead, we create an initramfs of the WHOLE SYSTEM which is larger but can start normal system with SystemD and a proper userland.

The following commands create a compresses initramfs from the chroot, ignoring some directories which contain stuff we don't want there to save memory on the host system. Feel free to adjust them.

cd /stable-chroot
find . \( -path './root/*' -o -path './boot/*' -o -path './var/*/*' -o \
	-path './usr/share/man' -o -path './usr/share/doc' \) \
	-prune -o -print0 | sudo cpio --null --create --verbose --format=newc | \
	gzip -9 >boot/initrd-root.img

Now, the symlink to the kernel is located at /stable-chroot/vmlinuz and the initramfs is at /stable-chroot/boot/initrd-root.img.

SystemD as /init

This is important since the kernel uses /init instead of /sbin/init when booting a system fro initial ram drive. We just need a symlink it there:

cd /stable-chroot
ln -s usr/bin/systemd init

Check that the link works. If you are using a legacy non-usrmerge system, you might find your SystemD from /bin/systemd instead.

Running with QEMU

The following command runs it on QEMU. On a production system you can safely set -display none to disable VGA and use -nic options to set up network for your system. This just starts it interactively:

qemu-system-x86_64 \
    -machine accel=kvm \
    -enable-kvm \
    -runas kvm \
    -m 2G \ # Must be twice the uncompressed size of initramfs by default
    -kernel /stable-chroot/vmlinuz \
    -initrd /stable-chroot/boot/initrd-root.img

There you go!

Network booting

Drop the vmlinuz and initrd files to your TFTP root and load them with a bootloader such as iPXE or GNU GRUB.

Kudos

Thanks to jpa for insights about Linux bootloading and clarifying the difference of initrd and initramfs.

In boot loaders, the term initrd is used for historical reasons and actually for a boot loader it doesn't make a difference which is the actual file format. It is just loaded to the memory and handed over to the kernel. Linux "initrd files" created with cpio do have initramfs format. See more information.

Like it?

Support me on GitHub to keep my hobbies going!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment