Building a custom initramfs with buildroot
Booting can be so easy
Modern GNU/Linux systems often need a bit of extra help finding their root file system during bootup. This is mostly a solved problem, but if you're the DIY type of person you may want to take a peek behind the scenes and possibly solve it yourself anyway.
In theory, booting is very simple (sort of): the kernel initializes all the hardware, finds a suitable root file system, mounts it and runs the init
binary. In reality things tend to be a bit more involved, the kernel is often unable to locate the root file system on its own and might need some user space applications to access it.
Let's find out how we can help.
Do you need help?
The machines that I use these days typically have their root file system on an encrypted volume. The kernel cannot unlock that volume on its own and is unable to access the file system:

The encrypted volume usually serves as a physical volume (PV) for LVM volume groups, which in turn contain the logical volumes:

Sometimes multiple disks form a software RAID – the kernel can assemble the RAID on its own:

In all these cases the kernel is stuck because it cannot handle the crypto layer. That's where the initramfs comes in: instead of running init
from the root file system, the kernel can load a tiny file system image, the initramfs, and run init
from there. This script will then proceed to unlock the encrypted file system and start the logical volumes contained within.
If your system does not use disk encryption, you may not need an initramfs at all; the kernel should be able to boot directly from your root file system. In that case you may stop reading now.
We have the technology
There are many tools to build initramfs images, tools such as genkernel, mkinitcpio, initramfs-tools or Dracut. These work fine in most cases and are certainly the more reasonable choice for most people. But this is not about being reasonable, this is about optimization.
One thing these tools have in common is that they are universal, i.e. they should be able to produce correctly working initramfs images for even the most bizarre setups. As a consequence they tend to come with complex configuration files and built-in logic to make sure that whatever they produce will be able to boot your system; people get upset otherwise.
If you are an expert user though, you know what it takes to boot your system, and typically that's just a few lines of shell scripting. This post is about how to make your kernel run these lines.
Enter buildroot
So what we really need is a little image that contains a shell to run our script plus the tools to unlock the encrypted file system and to our start logical volumes. And whatever dependencies these tools require.
We could just copy these things from a running system and that would probably work fine, but we would be spending a lot of time tracking down shared libraries and other dependencies. Instead it would be nice to build a system from scratch which contains everything it needs.
Enter buildroot. As the website says, "Buildroot is a simple, efficient and easy-to-use tool to generate embedded Linux systems through cross-compilation". Doesn't sound too bad.
Buildroot is centered around busybox, a small application that replicates the core functionality of many command-line tools. Buildroot images can easily be extended with thousands of extra packages, its build configuration is just a few files (possibly in a git repo) which makes it easy to keep track of what exactly you have been building.
That could be handy for the forgetful among us.
Getting ready
I won't bore you with the details on how to download (and compile) buildroot, it does after all come with one of the best manuals I have encountered in a long while.
Instead I will bore you with a quick overview of what you will need:
- buildroot. I typically use their git repo but you can also just download one of the tarballs. At the moment I am using version
2025.02-rc1
. - Your own self-compiled kernel. You need to compile all the drivers needed to access your disk and the encrypted volume into the kernel, not as modules.
- A build configuration. You can use my generic initramfs build configuration, it builds a very minimal system which includes cryptsetup to unlock the disks.
- An init script. Feel free to use my generic initramfs init script but read on before you do so.
My buildroot-external git repo is organized in a way that allows you to use it directly from a buildroot directory:
$ make BR2_EXTERNAL=/home/stefan/projects/buildroot generic_initramfs_defconfig
Tweaking things
While the script in my initramfs is quite generic, there are a few things you need to adjust.
Which disk?
As there may be multiple encrypted volumes on your disk, you need to specify which one to unlock (line 17). In addition to that, you also need to tell the script which volume to mount as your root filesystem and potentially adjust the mount options (line 24):

If you are uncertain about any of these options, the script allows you to interrupt the boot process and try a few things: Just append shell
to your kernel command line and the boot process will be interruped right before the disks are unlocked.
Hardware optimization
Maybe you want to optimize the binaries buildroot builds for your specific hardware. You can choose the correct CPU type for your system:

This will most likely not have any measurable impact on performance whatsoever, but still, it's nice to optimize things.
Build & deploy
Now you can build your image. This will take some time (about 10 minutes on my reasonably fast i5), afterwards you will find a brand new image at output/images/rootfs.cpio
– that's your initramfs.
Copy it to your boot folder, include it in your grub config and reboot.
Additional improvements
So far we have a very basic initramfs. You can of course add all kinds of extra things to it, ideally through buildroot packages.
Microcode
Some CPUs need microcode updates to work correctly (or less incorrectly). This is typically handled in the initramfs.
If you have an Intel CPU you can find the files you need in the Intel Linux microcode git repository. The tricky part is figuring out which file you need.
Take a look at your /proc/cpuinfo
:
processor : 13 vendor_id : GenuineIntel cpu family : 6 model : 170 model name : Intel(R) Core(TM) Ultra 5 125U stepping : 4
You will need the 3 marked values: cpu family
, model
and stepping
. Convert them to hex:
$ printf "%02x-%02x-%02x\n" 6 170 4 06-aa-04
And that's the name of the file you need. Download it from Intel's repo and add it as kernel/x86/microcode/GenuineIntel.bin
to your initramfs.
The next time you reboot you should see something like this:
[ 2.084350] microcode: Current revision: 0x00000020 [ 2.084356] microcode: Updated early from: 0x0000001c
I you don't, there is a good chance that your BIOS already contains the latest microcode version for your CPU and you don't need to update anything.
SSH
You could add an SSH daemon to your initramfs, Dropbear is a good choice for that – it's small and there is a buildroot package for it. If you configure the network interface from the init script you would be able to unlock the disk remotely via SSH.
This type of setup does involve certain risks though: your SSH host keys would be sitting on your disk unencrypted and anyone with phyisical access to the machine could copy they keys and use them to MITM you and steal the password when you unlock the disk.
Personally I had this configured for a while on a laptop that I wanted to reboot remotely but I don't have the config details available anymore.
Final thoughts
I have been using buildroot-based initramfs images for several years now and I really like the simplicity. They require little to no maintenance, I usually build a new one every couple of years just to stay in practice.