L31: Linux Kernel Boot Files & initramfs — Deep Dive

 

Linux Kernel Boot Files & initramfs — Deep Dive

Chapter 3 · Lecture 31  |  What really happens after make install? vmlinuz, System.map, initramfs — explained step by step for kernel 6.x

🆓 Free Course
🐧 Kernel 6.x
⏱ 25 min read
🎯 Beginner Friendly

Topics Covered in This Lecture

make install
vmlinux vs vmlinuz
initramfs
initrd
System.map
GRUB2
bzImage
CONFIG_BLK_DEV_INITRD
dracut
mkinitramfs
Kernel Module Loading at Boot
Embedded initramfs

In the previous lectures you configured and compiled the Linux kernel. Now comes the next step — installing it so the system can actually boot from it. But what does installing a kernel really mean? Which files get placed where? And what on earth is this initramfs image that always appears in /boot?

This lecture answers every one of those questions clearly. By the end you will understand every file that lands in /boot after a kernel build, why initramfs exists, and how it matters in both desktop Linux and embedded systems work — all updated for the modern kernel 6.x era.

📷 Suggested Image

A terminal screenshot showing the output of ls -lh /boot on a real Linux 6.x system — highlighting vmlinuz, initramfs, System.map and config files. Alt text: Linux /boot directory contents after kernel installation showing vmlinuz initramfs System.map files

1. What Does make install Actually Do?

Once you finish compiling the kernel with make, you run sudo make install to deploy it. Internally this triggers an architecture-specific shell script — on x86-64 it is arch/x86/boot/install.sh. That script’s only job is to copy the newly built kernel artifacts into your /boot directory so the bootloader can find them at the next reboot.

Here is what lands in /boot and what each file does:

Files Installed into /boot After make install — Kernel 6.x

vmlinuz-6.x.y The compressed, bootable kernel image. This is what GRUB loads into RAM. It is a copy of the bzImage produced during the build.
initramfs-6.x.y.img The initial RAM filesystem. Loaded alongside the kernel by GRUB. Provides an early userspace environment before the real root filesystem is mounted.
System.map-6.x.y The kernel symbol table. A plain-text file mapping every kernel function and variable name to its memory address. Critical for crash debugging.
config-6.x.y A saved copy of the .config used to build this kernel. Useful for reference, auditing, or rebuilding later.

If a file with the same name already exists in /boot, the install script backs it up with a .old suffix before overwriting. So you will sometimes see vmlinuz-6.x.y.old — that is your previous kernel kept safely as a fallback. GRUB2’s configuration at /boot/grub/grub.cfg is also updated automatically to add a new boot menu entry.

Typical /boot Directory Structure After make install (Kernel 6.x)

/boot/vmlinuz-6.12.0-custom Compressed kernel — loaded by GRUB at boot
/boot/initramfs-6.12.0-custom.img Early userspace RAM filesystem
/boot/System.map-6.12.0-custom Kernel symbol table for debugging
/boot/config-6.12.0-custom Build config — saved for reference
/boot/vmlinuz-6.12.0-custom.old Previous kernel backup (auto-created)
/boot/initramfs-6.12.0-custom.img.old Previous initramfs backup (auto-created)
/boot/grub/grub.cfg GRUB2 boot menu — auto-updated to include new kernel
/boot/efi/EFI/ EFI boot entries (on UEFI systems only)

2. vmlinux vs vmlinuz — What Is the Difference?

This is one of the most common points of confusion for students new to kernel work. Let’s clear it up completely.

vmlinux vs vmlinuz — Side by Side Comparison

Property vmlinux vmlinuz
What it is Uncompressed ELF binary Compressed, self-extracting image
Where it lives Root of the kernel source tree /boot/vmlinuz-<version>
Typical size 100 MB or more 10–15 MB
Used for Debugging (gdb, perf, crash tools) Booting — GRUB loads this into RAM
Debug symbols Full debug info present Stripped — no debug symbols
Format ELF (Executable and Linkable Format) bzImage (big zImage) on x86
Compression None gzip / lz4 / zstd (kernel 6.x)

The naming comes from old Unix history. On many Unix systems the kernel was called vmunix. Linux followed that tradition, naming its kernel vmlinux. The compressed version became vmlinuz — the z hinting at compression. On modern kernel 6.x systems, distros often default to LZ4 or Zstd compression because they are much faster to decompress than gzip, though the file is still called vmlinuz regardless of the compression used.

💡 How to Check Your Kernel’s Compression Type

# Look in your .config for the compression setting:
grep "CONFIG_KERNEL_" /boot/config-$(uname -r) | grep "=y"

# You will see one of:
# CONFIG_KERNEL_GZIP=y    (classic, widest support)
# CONFIG_KERNEL_LZ4=y     (fast decompression — common on Ubuntu 22+)
# CONFIG_KERNEL_ZSTD=y    (best ratio + speed — Fedora 37+, Debian 12+)

📷 Suggested Image

A diagram or annotated screenshot showing vmlinux in the kernel source tree root vs vmlinuz in /boot — with file sizes visible. Alt text: vmlinux vs vmlinuz comparison showing location size and purpose in Linux kernel development

3. What Is System.map and Why Do You Need It?

Think of System.map as the kernel’s phone book. It is a plain text file — generated automatically during compilation — that maps every kernel symbol (function name, global variable name) to its virtual memory address.

# Peek at the first few entries in System.map:
$ head -10 /boot/System.map-$(uname -r)

0000000000000000 D cpu_online_bits
ffffffff81000000 T startup_64
ffffffff81001000 T secondary_startup_64
ffffffff8103e0f0 T kernel_init
ffffffff8108a200 T do_fork
ffffffff81200000 D init_task

Each line has three parts: the memory address, a single letter for the symbol type (T = text/code, D = data, B = BSS), and the symbol name. When the kernel crashes and produces an Oops or panic message, the crash log shows raw hex addresses. You match those addresses against System.map to find out exactly which kernel function was executing when things went wrong. This is fundamental to kernel debugging work.

How System.map Is Used During a Kernel Crash

1. Kernel Oops Occurs
dmesg shows a hex address like
ffffffff8108a200
2. Look Up System.map
grep ffffffff8108a200
/boot/System.map-$(uname -r)
3. Find the Function
Result: T do_fork
Now you know which kernel function crashed

4. What Is initramfs and Why Does the Kernel Need It?

When you first look at the Linux boot process there appears to be a fundamental contradiction. Let’s work through it.

The Boot Paradox — Why initramfs Exists

The Kernel needs to mount the Root Filesystem
to access its files and start the system
The Root Filesystem may need a Driver Module
stored as a .ko file on the root filesystem
🥚 Which comes first — the filesystem or the driver? 🐔
This is why initramfs was invented
initramfs = The Solution
A tiny, self-contained filesystem pre-loaded into RAM by the bootloader before anything else.
It contains just enough drivers and scripts to load the real storage driver,
detect the filesystem, mount the real root, and hand over to systemd.
No chicken-and-egg problem.

The initramfs (initial RAM filesystem) is a compressed cpio archive. GRUB loads it into RAM alongside the kernel before the kernel even starts running. The kernel then unpacks it into a temporary in-memory filesystem (tmpfs) very early in boot — before any hardware storage driver is active — and runs the /init script inside it to do all the early setup work.

5. initrd vs initramfs — Know the Difference

initrd (Old) vs initramfs (Modern) — Key Differences

Feature initrd (old) initramfs (modern)
Full name Initial RAM Disk Initial RAM Filesystem
How it works Emulated block device in RAM cpio archive unpacked into tmpfs
Driver requirement Needs a block device driver AND a filesystem driver to mount itself No driver needed — the kernel unpacks it directly
Speed Slower Faster
Memory use Higher (fixed size block device) Lower (tmpfs only uses what it needs)
First appeared Kernel 2.0 era Kernel 2.6.13 (2005)
Status today Obsolete — kept for legacy Standard on all modern distros

In practice you will still see files named initrd.img on Debian/Ubuntu systems — this is just a legacy naming convention. The file format inside is initramfs (cpio), not the old initrd block-device format. The kernel handles both but always prefers initramfs.

6. What Can You Actually Do With initramfs?

Because initramfs runs real userspace programs before the main system starts, it unlocks capabilities the bare kernel cannot handle on its own during boot:

initramfs Capabilities — What It Enables at Boot

💾 Load Storage Driver Modules
Load NVMe, SCSI, RAID, or USB storage controller .ko modules so the kernel can see your disk at all.
🔒 Decrypt Encrypted Disks
Prompt for a LUKS passphrase and unlock an encrypted root partition before mounting. This is how full-disk encryption works on every modern Linux desktop.
🧩 Auto-detect Filesystem Type
Identify whether your root is ext4, btrfs, xfs, f2fs, or another format, then load the right filesystem module automatically.
🛠 Rescue Shell
Drop into a minimal BusyBox shell if something goes wrong at boot — so you can diagnose the problem without needing a live USB.
📡 Mount NFS Root (Embedded / Netboot)
Bring up a network interface early and mount the root filesystem from an NFS server — essential for diskless embedded targets and PXE boot environments.
🖥 Console & Keyboard Setup
Set the console font and keyboard layout before the graphical display manager starts — important for international keyboard support.

7. The Kernel Config Behind initramfs — CONFIG_BLK_DEV_INITRD

initramfs support in the kernel is controlled by one Kconfig option. You can check it with:

# Check if initramfs support is compiled in:
$ grep CONFIG_BLK_DEV_INITRD /boot/config-$(uname -r)
CONFIG_BLK_DEV_INITRD=y

# To embed an initramfs directly inside the kernel binary (for embedded targets):
CONFIG_INITRAMFS_SOURCE="/path/to/your/rootfs/dir"

# Choose compression (kernel 6.x options):
# CONFIG_INITRAMFS_COMPRESSION_GZIP=y    (default, widest support)
# CONFIG_INITRAMFS_COMPRESSION_LZ4=y     (fast decompression — good for embedded)
# CONFIG_INITRAMFS_COMPRESSION_ZSTD=y    (best ratio + speed — modern distros)

CONFIG_BLK_DEV_INITRD=y is the default in every mainstream kernel config. Disabling it means the kernel cannot use an initramfs at all — only acceptable on specialized embedded targets where all required drivers are built directly into the kernel (=y, not as modules).

8. The Complete Linux Boot Sequence — Where initramfs Fits

Linux Boot Sequence from Power-On to Login Prompt (Kernel 6.x)

1 BIOS / UEFI Firmware — Hardware self-test (POST), detects bootable device, hands off to GRUB2
2 GRUB2 Bootloader — Reads /boot/grub/grub.cfg, loads vmlinuz-6.x.y and initramfs-6.x.y.img into RAM
3 Kernel Decompresses Itself — vmlinuz self-extracts into memory, kernel code starts running
4 Kernel Early Init — Memory management, CPU setup, device tree parsing, interrupt tables set up
5 initramfs Phase (the key middle step)
• Kernel unpacks initramfs cpio archive into tmpfs
/init script runs inside initramfs
• Loads storage and filesystem kernel modules
• Unlocks LUKS encryption if configured
• Mounts the real root filesystem (e.g. /dev/nvme0n1p2)
• Hands control to real init (systemd)
6 initramfs Discarded — All RAM used by the initramfs is freed once the real root is mounted
7 systemd / init starts — Runs from the real root filesystem, starts all services, brings up networking, display manager
8 Login Prompt / Desktop — System fully booted ✓

📷 Suggested Image

A photo of a terminal running systemd-analyze showing the boot timeline, with the initramfs phase highlighted. Alt text: Linux kernel boot timeline showing initramfs phase duration using systemd-analyze

9. How Is the initramfs Image Built?

The initramfs image is built by a userspace tool — not by the kernel build system. On Debian/Ubuntu systems the tool is update-initramfs (a wrapper around mkinitramfs). On Fedora/RHEL and modern Arch/Ubuntu systems the tool is dracut.

How dracut / mkinitramfs Builds the initramfs Image

Step 1 Scan Hardware and Config — The tool inspects your installed hardware and kernel config to decide which modules are needed for boot: storage controller driver, filesystem driver, LUKS crypto modules, LVM, RAID, etc.
Step 2 Collect Files — Kernel modules (.ko), essential binaries (BusyBox, udev), shared libraries, and the /init shell script are gathered into a staging directory.
Step 3 Pack as cpio Archive — All files are packed into a cpio archive, then compressed with gzip, lz4, or zstd depending on your configuration.
Step 4 Write to /boot — The final image is placed at /boot/initramfs-<version>.img (RHEL/Fedora) or /boot/initrd.img-<version> (Debian/Ubuntu). GRUB reads this path from grub.cfg.
# Debian/Ubuntu: update initramfs for running kernel
sudo update-initramfs -u

# Debian/Ubuntu: create image for a specific kernel version
sudo update-initramfs -c -k 6.12.0-custom

# Fedora/RHEL/Arch: build with dracut (modern tool)
sudo dracut --force /boot/initramfs-$(uname -r).img $(uname -r)

# Inspect what is inside your initramfs:
lsinitramfs /boot/initrd.img-$(uname -r) | head -50      # Debian/Ubuntu
lsinitrd /boot/initramfs-$(uname -r).img | head -50       # Fedora/RHEL

10. initramfs in Embedded Linux — A Different Story

In embedded systems work, initramfs is used differently to desktop Linux. On a desktop it is a temporary bridge that is discarded after boot. On an embedded target it can become the permanent root filesystem that runs for the entire system lifetime.

Desktop Linux vs Embedded Linux — How initramfs Is Used

Desktop / Server Linux

Role: Temporary bridge
Duration: A few hundred milliseconds
What it does: Loads drivers → mounts real disk → discards itself
Lives in: Separate file in /boot/
Root filesystem: On a disk partition (ext4, btrfs, etc.)

Embedded Linux (Buildroot / Yocto)

Role: Permanent root filesystem
Duration: Entire device lifetime
What it does: BusyBox + your application run from RAM
Lives in: Baked inside the kernel binary itself
Root filesystem: IS the initramfs — no disk needed

# Kconfig for embedded: embed initramfs directly inside the kernel binary
CONFIG_INITRAMFS_SOURCE="/path/to/your/rootfs/staging/dir"
CONFIG_INITRAMFS_COMPRESSION_LZ4=y   # Fast decompression on low-power targets

# After setting this, 'make' bundles your entire rootfs into vmlinuz itself
# Deploy result: one file to flash — no separate rootfs partition needed

11. Quick Reference — What to Remember

Key Concepts Summary Table

Concept What It Is Key Point
make install Kernel install command Copies vmlinuz, initramfs, System.map, config to /boot and updates GRUB2
vmlinux Uncompressed kernel ELF Source tree root; used for debugging; not bootable directly
vmlinuz Compressed kernel image Lives in /boot; GRUB loads this; “z” = compressed
System.map Kernel symbol table Maps function names → memory addresses; critical for crash debugging
initramfs Initial RAM filesystem Solves boot chicken-and-egg; loads drivers before root is mounted
CONFIG_BLK_DEV_INITRD Kernel config option Enables initramfs support; default = y everywhere
dracut / mkinitramfs Build tools Generate the initramfs image from modules and scripts
Embedded initramfs Permanent rootfs in RAM Set CONFIG_INITRAMFS_SOURCE; bake rootfs into kernel binary

Frequently Asked Questions (FAQ)

Q: Can I boot Linux without initramfs?

Yes — if all your storage drivers and your root filesystem’s driver are compiled directly into the kernel (=y in .config, not as loadable modules), initramfs is not needed. This is common on minimal embedded systems where the hardware is fixed and known. On general-purpose distros it is always used because they ship a single kernel for thousands of hardware combinations.

Q: Why is the file named initrd.img on Ubuntu but initramfs on Fedora?

This is purely a naming convention difference between distributions. The file format inside is identical cpio-based initramfs in both cases. Ubuntu/Debian inherited the initrd name from history; Fedora/RHEL chose the more accurate initramfs name. The kernel handles both correctly — it detects the format by inspecting the file header, not the filename.

Q: What happens if the initramfs image is missing or corrupt?

The system will fail to boot and drop into a kernel panic with an error like “VFS: Unable to mount root fs”. To fix this, boot from a live USB, mount your root partition, chroot into it, and regenerate the initramfs with update-initramfs -u or dracut --force. This is a common recovery task for Linux admins.

Q: How do I reduce initramfs boot time on an embedded target?

Three practical approaches: (1) Switch compression from gzip to LZ4 (CONFIG_INITRAMFS_COMPRESSION_LZ4=y) — decompression is 3–5x faster. (2) Use dracut --hostonly or prune unnecessary modules from the initramfs to reduce its size. (3) For targets with fixed hardware, skip initramfs entirely and compile all drivers in-kernel. Buildroot and Yocto both have options to generate minimal, stripped-down initramfs images optimized for fast boot.

Q: What is the difference between mkinitramfs and dracut?

mkinitramfs is a shell-script based tool from the Debian ecosystem — simple and reliable but less configurable. dracut is a more modern, modular, distro-agnostic framework used by Fedora, RHEL, Arch, and increasingly Ubuntu. Both produce a functionally equivalent cpio initramfs image. dracut is better for complex setups like LUKS-on-LVM, iSCSI root, multipath, or when you need fine-grained control over what goes into the image.

Q: How is vmlinuz different from bzImage?

bzImage is the name of the compressed kernel image as produced in the kernel build system — specifically at arch/x86/boot/bzImage. When make install runs, that file is copied to /boot/ and renamed to vmlinuz-<version>. So vmlinuz and bzImage are the same binary — just different names at different stages of the workflow. The “bz” in bzImage stands for “big zImage” — it is not related to bzip2 compression.

🎯 Interview Questions — Kernel Boot and initramfs

These are real questions asked in embedded Linux and kernel development interviews at companies like Qualcomm, Texas Instruments, NXP, Bosch, and FAANG-level embedded teams. Practice answering them in your own words before your interview.

Q1. What is the difference between vmlinux and vmlinuz?

vmlinux is the uncompressed ELF kernel binary sitting in the root of the kernel source tree after compilation. It is large (often over 100 MB), retains full debug symbols, and is used by tools like gdb, perf, and crash for post-mortem debugging. It is not directly bootable.

vmlinuz is the compressed, self-extracting kernel image placed in /boot. GRUB loads this into RAM at boot time. The “z” hints at compression — historically gzip, though modern kernel 6.x builds commonly use LZ4 or Zstd. On x86 this file is architecturally a bzImage.

Q2. Why does Linux need initramfs? What problem does it solve?

The fundamental boot paradox: the kernel needs to mount the root filesystem to load its files — but mounting the root filesystem may require a kernel module (e.g. the NVMe driver, RAID driver, or filesystem driver) that is stored on the root filesystem. A circular dependency.

initramfs solves this by giving the kernel a tiny, self-contained filesystem in RAM before any disk driver is loaded. Its /init script loads the necessary kernel modules, handles disk decryption if needed, and mounts the real root filesystem. Once that is done, initramfs is discarded and the real system takes over.

Q3. What is System.map and when do you use it as a kernel developer?

System.map is a plain text file generated at compile time that maps every kernel symbol name to its virtual memory address. As a kernel developer you use it primarily when investigating kernel Oops or panic messages — the crash log contains hex addresses, and System.map lets you translate those addresses into function names to identify exactly where in the kernel the crash occurred.

Q4. What is the difference between initrd and initramfs?

initrd (initial RAM disk) was the older approach — it emulated a block device in RAM and required the kernel to have a block device driver and a filesystem driver just to access it. initramfs replaced this in kernel 2.6. It is a cpio archive that the kernel unpacks directly into a tmpfs — no filesystem driver needed to access itself. initramfs is simpler, faster, uses less memory, and is the standard on all modern Linux systems.

Q5. How is initramfs used differently in embedded Linux vs desktop Linux?

On a desktop, initramfs is a temporary bridge — it loads drivers, mounts the real root disk partition, then gets discarded. On embedded targets, initramfs often becomes the permanent root filesystem. The entire userspace (BusyBox, daemons, config files) is packed into the initramfs and embedded directly inside the kernel binary using CONFIG_INITRAMFS_SOURCE. This produces a single deployable file, fast boot, and a clean read-only RAM filesystem — ideal for embedded products.

Q6. Which kernel config enables initramfs support and what is the default?

CONFIG_BLK_DEV_INITRD=y. It is enabled by default in all mainstream kernel configurations. Disabling it means the kernel cannot use an initramfs at all. This is only done on minimal embedded targets where the hardware is fixed and all required drivers can be compiled directly into the kernel image.

Q7. Walk me through what happens from GRUB to the login prompt on a modern Linux 6.x system.

GRUB reads its config, loads vmlinuz and the initramfs image into RAM. The self-extracting vmlinuz decompresses into memory and begins running. The kernel initialises memory, the scheduler, and basic hardware. It unpacks the initramfs into tmpfs and runs /init inside it — loading storage drivers, handling encryption, mounting the real root filesystem. initramfs is then freed, and control is handed to systemd on the real root. systemd starts all services in parallel and eventually the login prompt or desktop appears.

Continue Learning — Free Linux Kernel Development Course

This lecture is part of the free Linux Kernel Programming series at EmbeddedPathashala. No fees, no registration — just practical kernel knowledge for embedded systems engineers.

Browse All Lectures
Next: Customising GRUB2

Leave a Reply

Your email address will not be published. Required fields are marked *