initramfs and GRUB — Boot Your Custom Linux 6.x Kernel
Lecture 28 | Steps 6 and 7: Generate initramfs, Configure GRUB and Verify Boot | Free Linux Kernel Development Course
What You Will Learn
initrd vs initramfs
Why initramfs exists
update-initramfs Ubuntu
dracut Fedora RHEL
mkinitcpio Arch
GRUB2 configuration
update-grub
grub-mkconfig
Boot verification
Where Are We?
You compiled the kernel in Lecture 26 (Step 4) and installed the modules in Lecture 27 (Step 5). This lecture completes Steps 6 and 7: generating an initramfs image that handles early boot setup, and updating the GRUB bootloader so it knows about your new kernel. After this you will reboot into your own custom Linux 6.x kernel.
Step 6 — What is initramfs and Why Does Linux Need It?
When your machine powers on, GRUB loads the kernel image into RAM and jumps to it. The kernel starts executing — but immediately hits a fundamental problem. It needs to mount the root filesystem to access the rest of the operating system. But the root filesystem sits on a device that may require special handling before it can be accessed.
The Chicken-and-Egg Boot Problem
❌ Root on LVM (Logical Volume Manager)
The kernel needs to activate LVM volume groups to see the root partition. But the device mapper modules needed for LVM are stored on the root partition that is not yet accessible. You need the disk to load the driver. You need the driver to see the disk.
❌ Root on LUKS Encrypted Disk
The kernel must load dm-crypt, decrypt the volume, and prompt for a passphrase before it can access the root filesystem. All of this requires cryptsetup and related tools — which are on the encrypted volume that cannot be read yet.
❌ Root on Software RAID or NVMe
The NVMe driver or md-raid assembly must happen before the root partition is visible. If these are compiled as modules rather than built-in, the kernel cannot load them because they are on the disk it cannot yet read.
✓ The Solution — initramfs
A small temporary root filesystem packed into RAM by the bootloader alongside the kernel. The kernel mounts this first. The tools inside it do all early setup — load drivers, activate LVM/LUKS/RAID, and mount the real root filesystem. Then control transfers to the real system and the initramfs is discarded from RAM.
Complete Linux 6.x Boot Flow with initramfs
1 — BIOS/UEFI → GRUB loads bzImage and initramfs into RAM
Both files are read from disk by the bootloader and placed in memory before the kernel runs. GRUB then hands control to the kernel entry point.
2 — Kernel decompresses itself and initialises hardware
The kernel unpacks the initramfs cpio archive into a tmpfs — a RAM-backed filesystem. Now a minimal filesystem is available in memory.
3 — initramfs /init script runs
Loads storage drivers, activates LVM volumes, decrypts LUKS partitions, assembles RAID arrays, and finally mounts the real root filesystem at a temporary location.
4 — switch_root transfers control to the real root filesystem
The kernel pivots from the initramfs tmpfs to the real root filesystem. The initramfs is unmounted and freed from RAM. systemd (PID 1) starts and normal boot continues to the login prompt.
initrd vs initramfs — The Technical Difference
initrd vs initramfs — Feature Comparison
initrd — Initial RAM Disk (Legacy)
A fixed-size block device image formatted as ext2. The kernel mounts it as a RAM disk using a block device driver. Has a fixed size that must be declared at creation time. Was the standard approach in Linux 2.4 and earlier. Still found on some legacy and embedded systems where simplicity is valued.
initramfs — Initial RAM Filesystem (Modern)
A compressed cpio archive that the kernel unpacks directly into a tmpfs (an in-memory filesystem). No fixed size limit. No block device driver needed. Simpler to create, easier to modify, and more flexible. Standard on all modern Linux distributions using Linux 2.6 and later.
💡 Why Ubuntu Still Says “initrd” in the Filename
Ubuntu names the file /boot/initrd.img-6.6.30 even though the content is actually an initramfs cpio archive internally. This is a historical naming convention that has persisted for compatibility. The file format is definitively initramfs — do not let the filename mislead you when reading documentation or tutorials.
What Is Inside an initramfs?
You can inspect the contents of your current running system’s initramfs:
# On Ubuntu or Debian
lsinitramfs /boot/initrd.img-$(uname -r) | head -30
# On Fedora or RHEL
lsinitrd /boot/initramfs-$(uname -r).img | head -30
What Lives Inside a Typical initramfs
🔌 Early Kernel Modules
Only modules needed for accessing the root filesystem. Selected based on your hardware and storage configuration.
ahci.ko — SATA disk
ext4.ko — root filesystem
dm-crypt.ko — LUKS encryption
dm-mod.ko — LVM base
🔧 Minimal Userspace Tools
Small binaries that do the actual work of setting up storage before handing off to systemd.
cryptsetup — LUKS decryption
lvm — volume group activation
fsck — filesystem integrity check
udevd — device event handling
📄 Init Script
The /init script (or /init.d/ hooks in dracut) is the entry point. The kernel executes this immediately after extracting the initramfs. It orchestrates the entire early boot sequence.
Generating the initramfs — Tools by Distribution
initramfs Generation Tools — Which Tool for Which Distro
Ubuntu and Debian — update-initramfs
Built on top of initramfs-tools. Simple, reliable, and the default on Ubuntu/Debian. Output file: /boot/initrd.img-<version>
sudo update-initramfs -c -k 6.6.30-mykernel
Fedora, RHEL, CentOS Stream — dracut
A modular, event-driven initramfs framework. More powerful and flexible. The standard on enterprise Linux. Output file: /boot/initramfs-<version>.img
sudo dracut --force /boot/initramfs-6.6.30-mykernel.img 6.6.30-mykernel
Arch Linux — mkinitcpio
Hook-based tool that is fast and highly configurable. Configured via /etc/mkinitcpio.conf.
sudo mkinitcpio -k 6.6.30-mykernel -g /boot/initramfs-6.6.30-mykernel.img
On Ubuntu or Debian — Step-by-Step
KVER="6.6.30-mykernel"
# Copy kernel image and symbol table to /boot
sudo cp arch/x86/boot/bzImage /boot/vmlinuz-${KVER}
sudo cp System.map /boot/System.map-${KVER}
# Generate the initramfs
# -c = create new image -k = kernel version string
sudo update-initramfs -c -k ${KVER}
# Confirm the file was created
ls -lh /boot/initrd.img-${KVER}
On Fedora or RHEL — Step-by-Step with dracut
KVER="6.6.30-mykernel"
# Copy kernel
sudo cp arch/x86/boot/bzImage /boot/vmlinuz-${KVER}
# Generate initramfs
# --force overwrites if the file already exists
sudo dracut --force /boot/initramfs-${KVER}.img ${KVER}
# Optional: hostonly mode — builds a smaller, machine-specific image
sudo dracut --force --hostonly /boot/initramfs-${KVER}.img ${KVER}
dracut — Generic vs Hostonly Mode
Generic Mode (Default) — Larger, More Portable
Includes drivers for a wide range of hardware. The resulting initramfs will boot on different machines with different storage controllers. Use this for servers, virtual machines, or systems where hardware varies. Typical size: 30 to 60 MB.
Hostonly Mode (-H flag) — Smaller, Machine-Specific
Includes only what the current machine actually uses. dracut detects your hardware and includes only the required drivers. Will only boot on hardware very similar to the build machine. Typical size: 8 to 20 MB and noticeably faster to load.
Step 7 — Configuring the GRUB Bootloader
GRUB (Grand Unified Bootloader 2) presents the kernel selection menu at boot. It reads a configuration file to know which kernels exist. After copying your kernel and generating the initramfs, you must update this configuration.
⚠ Never Edit grub.cfg Directly
/boot/grub/grub.cfg is auto-generated and will be overwritten the next time GRUB is updated. Always regenerate it using the commands below. Place custom entries in /etc/grub.d/40_custom if you need to add something that the auto-scanner does not pick up.
# Ubuntu or Debian
sudo update-grub
# Fedora, RHEL, CentOS (BIOS systems)
sudo grub2-mkconfig -o /boot/grub2/grub.cfg
# Fedora, RHEL, CentOS (UEFI systems)
sudo grub2-mkconfig -o /boot/efi/EFI/fedora/grub.cfg
# Arch Linux
sudo grub-mkconfig -o /boot/grub/grub.cfg
These commands scan /boot for all vmlinuz-* kernel files and their matching initramfs files, then write a new grub.cfg with a menu entry for each one found — including your new custom kernel.
Anatomy of a GRUB Menu Entry for Your Custom Kernel
The Generated grub.cfg Entry (Do Not Edit Directly)
menuentry 'Linux 6.6.30-mykernel' {
linux /boot/vmlinuz-6.6.30-mykernel root=/dev/sda2 ro quiet
initrd /boot/initrd.img-6.6.30-mykernel
}
The linux Line — Kernel and Command Line Parameters
Points to the kernel image file and passes parameters. root=/dev/sda2 tells the kernel where to find the real root filesystem. ro means mount it read-only initially so fsck can check it. quiet suppresses most boot messages for a cleaner user experience.
The initrd Line — The initramfs Image
Points to the initramfs file that GRUB loads into RAM alongside the kernel. The kernel uses this during early boot before the real root filesystem is accessible.
Complete End-to-End Sequence — Ubuntu and Debian
KVER="6.6.30-mykernel"
# Step 5: Install kernel modules
sudo make modules_install
# Step 6a: Copy kernel and System.map to /boot
sudo cp arch/x86/boot/bzImage /boot/vmlinuz-${KVER}
sudo cp System.map /boot/System.map-${KVER}
# Step 6b: Generate initramfs
sudo update-initramfs -c -k ${KVER}
# Step 7: Update GRUB
sudo update-grub
# Verify all files are in place
ls -lh /boot/ | grep ${KVER}
# Reboot and select your kernel from the GRUB menu
sudo reboot
And the equivalent for Fedora or RHEL:
KVER="6.6.30-mykernel"
sudo make modules_install
sudo cp arch/x86/boot/bzImage /boot/vmlinuz-${KVER}
sudo dracut --force /boot/initramfs-${KVER}.img ${KVER}
sudo grub2-mkconfig -o /boot/grub2/grub.cfg
sudo reboot
Verifying Your Custom Kernel After Boot
# Confirm the running kernel version — should show your LOCALVERSION suffix
uname -r
# Expected output: 6.6.30-mykernel
# Full kernel information including build date and architecture
uname -a
# View the kernel command line that was used at boot
cat /proc/cmdline
# Check early boot messages for errors
dmesg | head -40
# Verify config options if you enabled CONFIG_IKCONFIG_PROC=y
zcat /proc/config.gz | grep CONFIG_YOUR_OPTION
Post-Boot Verification Checklist
✓ Things That Must Work
System boots fully to login prompt
Network works — ip link shows interfaces
Disk accessible — df -h shows mounted partitions
dmesg has no critical errors
⚠ If Something Goes Wrong — Fallback to Old Kernel
Hold Shift (BIOS) or ESC (UEFI) during boot to access the GRUB menu. Select Advanced options and choose your previous working kernel. Your custom kernel entry will also still be there for subsequent debugging attempts. The old kernel is always available as long as you did not remove it from /boot.
Common Boot Problems and How to Fix Them
Boot Failure Troubleshooting Reference
Kernel panic: VFS cannot mount root fs on unknown-block
Cause: Root filesystem driver missing from initramfs, or the root= parameter in GRUB points to the wrong device. Fix: Reboot on the old kernel. Verify your partition path with lsblk. Regenerate the initramfs. Check the GRUB entry for your kernel.
Black screen after GRUB selects your kernel
Cause: Kernel mode setting (KMS) problem with the GPU driver. Fix: Press E in the GRUB menu to edit the boot entry. Add nomodeset to the end of the linux line and press Ctrl+X to boot. This disables kernel GPU mode setting and uses a basic framebuffer.
Dropped to dracut emergency shell
Cause: initramfs ran but could not mount the root filesystem — usually a missing storage driver. From the emergency shell use lsblk to check if the disk is visible and lsmod to see which modules are loaded. Reboot to the old kernel and regenerate the initramfs to include the missing driver.
Your kernel not visible in the GRUB menu
Cause: Forgot to run update-grub or grub2-mkconfig after copying the kernel to /boot. Fix: Boot using the existing kernel, run the GRUB update command, and reboot again.
📷 Suggested Featured Image
A terminal screenshot showing the output of uname -a on your freshly booted custom kernel — with the LOCALVERSION suffix clearly visible in the version string — is the definitive proof that the entire build and boot sequence worked. If you can also capture the GRUB menu showing your kernel entry, that pairing makes an excellent two-image set for this post.
Frequently Asked Questions
Q: Does every Linux system need an initramfs?
No. If the root filesystem is on a simple disk and all required drivers are compiled directly into the kernel (set to Y, not M in menuconfig), you can boot without an initramfs. Many embedded Linux systems do exactly this to get a faster, simpler boot sequence. Desktop and server distributions always use an initramfs because they must support complex storage configurations out of the box, and distribution kernels compile most drivers as modules to keep the kernel image size manageable.
Q: What is the difference between update-grub and grub-mkconfig?
They do exactly the same thing. update-grub is a convenience shell script that calls grub-mkconfig -o /boot/grub/grub.cfg. It exists on Ubuntu and Debian as a shorter command to type. On Fedora and RHEL only grub2-mkconfig exists (note the 2 in the name). All variants scan /etc/grub.d/ scripts and apply settings from /etc/default/grub to regenerate the bootloader configuration file.
Q: Is dracut available on Ubuntu and can I use it instead of update-initramfs?
Yes. Install with sudo apt install dracut. dracut is worth learning because it is the standard tool on enterprise distributions (RHEL, Fedora, SUSE) which are widely deployed in production. It has better support for complex storage scenarios, network boot, and emergency recovery. update-initramfs is simpler and the default, but dracut is the industry direction for modern enterprise Linux.
Interview Questions
Q1. What is initramfs and what problem does it solve?
initramfs (initial RAM filesystem) is a small compressed cpio archive the bootloader loads into RAM alongside the kernel. It solves the chicken-and-egg boot problem: modern Linux systems store the root filesystem on complex storage (LVM, LUKS, RAID, NVMe) that requires drivers and userspace tools to access. Those tools are on the root filesystem that cannot be mounted yet. The initramfs provides a self-contained minimal environment in RAM to do all early-boot storage setup before transferring control to the real root filesystem.
Q2. What is the technical difference between initrd and initramfs?
initrd (initial RAM disk) is a fixed-size block device image (ext2 formatted) that the kernel mounts as a RAM disk. It requires a block device driver. initramfs is a compressed cpio archive unpacked by the kernel directly into a tmpfs — an in-memory filesystem built into the kernel. No block device driver is needed. initramfs has no fixed size limit, is simpler to create and modify, and starts faster because there is no intermediate disk image to mount. All modern Linux distributions use initramfs even when files are still named with “initrd” in the filename.
Q3. What does dracut do and how does it differ from update-initramfs?
Both build initramfs images but with different architectures. dracut organises initramfs functionality into dracut modules — directories of scripts that run at defined boot-time hooks. It supports hostonly mode to build minimal machine-specific images, has rich support for network boot, LUKS, LVM, RAID, and iSCSI, and is actively maintained as the standard framework on Fedora, RHEL, and SUSE. update-initramfs is the Debian/Ubuntu tool built on initramfs-tools — simpler, less flexible, and more opinionated. dracut is the direction of modern enterprise Linux initramfs management.
Q4. Explain the complete sequence of events from power-on to login prompt on a modern Linux system.
1. BIOS or UEFI performs POST (power-on self-test) and locates the bootloader. 2. GRUB reads its configuration, shows the boot menu, and loads the selected kernel (bzImage) and initramfs into RAM. 3. The kernel decompresses itself and initialises the CPU, memory, interrupt handlers, and core subsystems. 4. The kernel extracts the initramfs cpio archive into a tmpfs. 5. The kernel runs /init inside the initramfs. 6. The init script loads storage and filesystem modules, activates LVM or LUKS as needed, and mounts the real root filesystem. 7. switch_root pivots to the real root, discarding the initramfs from RAM. 8. systemd (PID 1) starts, processes unit files, launches services, and presents the login prompt.
Q5. Why should you never directly edit /boot/grub/grub.cfg?
The file is entirely auto-generated by grub-mkconfig from scripts in /etc/grub.d/ and settings in /etc/default/grub. Every time you run update-grub or grub-mkconfig, the file is completely replaced — any manual edits are silently destroyed. The correct places to make changes: add custom boot entries in /etc/grub.d/40_custom (this file is preserved and included during regeneration), change timeout, default kernel, or kernel parameters in /etc/default/grub, then run update-grub to apply the changes.
Q6. A kernel panics at boot with VFS: Unable to mount root fs. What are the possible causes and how do you fix each?
Three main causes: First, the root= parameter in GRUB points to the wrong device — disk names can change when hardware is added or removed (sda becoming sdb, or switching to nvme0n1). Use UUID-based root parameters to avoid this: root=UUID=.... Second, the filesystem driver for the root partition (ext4, btrfs, xfs) is not in the initramfs and is not compiled into the kernel. Regenerate the initramfs with the correct filesystem module. Third, the storage controller driver (NVMe, AHCI) is missing from the initramfs so the disk is not visible at all. Fix by regenerating the initramfs in a working boot environment.
Q7. What kernel configuration options are required to use an initramfs?
Two options must be enabled. CONFIG_BLK_DEV_INITRD=y — labelled “Initial RAM filesystem and RAM disk (initramfs/initrd) support” under General Setup in menuconfig. Without this the kernel ignores the initramfs passed by the bootloader. CONFIG_DEVTMPFS=y — ensures that a /dev filesystem is available during early userspace so device nodes can be created. Both are enabled by default in defconfig and in all distribution configs, so you only need to worry about these when starting from scratch with a minimal config.
Continue the Free Linux Kernel Development Course
You have now built, installed, and booted your own custom Linux 6.x kernel from source. In the next lecture we explore cross-compiling the kernel for ARM — building on your development PC to run on a Raspberry Pi or custom embedded board.
