Kernel Module Installation & initramfs
Linux Kernel Programming — Chapter 3, Lecture 30 | Free Course by EmbeddedPathashala
Chapter 3
Linux 6.x
Beginner
FAQ Inside
What You Will Learn in This Lecture
depmod utility
/lib/modules/ layout
INSTALL_MOD_PATH
initramfs vs initrd
Linux boot process
GRUB bootloader
make install
dracut & update-initramfs
modprobe vs insmod
switch_root
Embedded Linux cross-build
Introduction
In the previous lecture you compiled the Linux kernel and produced a bzImage. But that alone is not enough to boot your system. Two critical steps must still happen before you reboot:
Step 5 — Install kernel modules so the running kernel can find and load them from the right location on disk.
Step 6 — Generate initramfs and update GRUB so the bootloader knows about your new kernel and the early-boot environment has what it needs.
Skipping either step almost always ends with a kernel that either panics on boot or silently loses device support. This lecture walks through both steps in plain language, updated for Linux 6.x kernels.
Screenshot of a terminal running
make modules_install showing INSTALL lines followed by the DEPMOD line. Label it: “Terminal output of make modules_install on Linux 6.x”. Alt text: make modules_install output showing kernel module installation and depmod step.Step 5 — Installing Kernel Modules
What Is a Kernel Module?
When you run make menuconfig, each kernel feature can be set to one of three states:
| Config Value | Meaning | Where It Goes |
|---|---|---|
Y (built-in) |
Feature is compiled permanently into the kernel | Inside vmlinux / bzImage |
M (module) |
Feature is compiled as a loadable object | Separate .ko file on disk |
N (disabled) |
Feature is not compiled at all | Not present anywhere |
A .ko (kernel object) file is not a normal program. It is a chunk of kernel code that can be dynamically inserted into a running kernel and removed when no longer needed. This keeps the core kernel image small while still supporting a huge range of hardware and features.
Your Wi-Fi card driver is almost certainly a
.ko module. It loads when the card is detected and can be unloaded if the card is removed. The kernel does not need to carry driver code for every possible Wi-Fi chip permanently inside its image.How Kernel Build Output Is Organized
Why Modules Need to Be Installed
After the build, all .ko files live inside your kernel source directory (for example /usr/src/linux-6.6.30/). The running kernel and userspace tools like modprobe never look there. They look in one specific location:
/lib/modules/$(uname -r)/
The $(uname -r) part expands to the running kernel’s version string, for example 6.6.30-mykernelbuild. Every kernel version on the machine gets its own subdirectory here, so multiple kernels can coexist safely without their modules colliding.
Running make modules_install
From inside your kernel source directory run:
cd /path/to/linux-6.6.30
sudo make modules_install
This command does exactly two things:
1 — Copy .ko files
Every compiled .ko file is copied from the source tree into /lib/modules/<version>/kernel/, keeping the same subdirectory structure (drivers/, fs/, net/, sound/, etc.).
2 — Run depmod automatically
After copying, the build system calls depmod on the new module directory. This generates the dependency metadata files that modprobe needs to load modules in the right order.
Sample output looks like this:
INSTALL arch/x86/crypto/aesni-intel.ko
INSTALL drivers/net/ethernet/intel/e1000e/e1000e.ko
INSTALL fs/ext4/ext4.ko
INSTALL sound/pci/hda/snd-hda-intel.ko
...
DEPMOD 6.6.30-mykernelbuild
The directory
/lib/modules/ is only writable by root. You need sudo. The only exception is when you redirect the install to a path you own using INSTALL_MOD_PATH (covered next).Understanding depmod
Some modules depend on other modules. For example a USB audio driver needs the USB core module loaded first, and the USB core might need the USB host controller driver. depmod figures all of this out automatically.
How depmod Maps Module Dependencies
snd-usb-audio.ko: snd-usbmidi-lib.ko snd.komodprobe reads modules.dep and loads in correct order:snd.ko → snd-usbmidi-lib.ko → snd-usb-audio.koThe metadata files depmod generates:
| File | Purpose |
|---|---|
modules.dep |
Lists every module and the modules it depends on |
modules.alias |
Maps hardware IDs (PCI, USB) to module names so the kernel can auto-load drivers |
modules.symbols |
Maps kernel symbol names to the modules that export them |
modules.builtin |
Lists all features compiled directly into the kernel (not as modules) |
INSTALL_MOD_PATH — Critical for Embedded Development
If you are cross-compiling a kernel for an embedded target (ARM Cortex-A, RISC-V, MIPS, etc.) on your x86 laptop, you must never install the target’s modules into your laptop’s /lib/modules/. That would destroy your laptop’s own module setup.
The INSTALL_MOD_PATH variable redirects the install to any directory you choose:
# Create a staging directory (no sudo needed — you own it)
export INSTALL_MOD_PATH=/home/ravi/staging/rootfs
# Install target modules into staging directory
make INSTALL_MOD_PATH=${INSTALL_MOD_PATH} modules_install
# Result: /home/ravi/staging/rootfs/lib/modules/6.6.30-arm/
# kernel/drivers/ ...
# modules.dep ...
# Then copy this to the target's SD card or flash
Embedded Cross-Compile Module Install Workflow
Running
sudo make modules_install without it during a cross-compile will silently overwrite your host machine’s kernel modules. Your host system may become unbootable. Always set INSTALL_MOD_PATH to a safe staging directory.| Scenario | Command | Install Destination |
|---|---|---|
| Native build (desktop / server) | sudo make modules_install |
/lib/modules/<version>/ |
| Cross-compile for embedded | make INSTALL_MOD_PATH=/staging modules_install |
/staging/lib/modules/<version>/ |
| Custom rootfs build | make INSTALL_MOD_PATH=/my/rootfs modules_install |
/my/rootfs/lib/modules/<version>/ |
A split-screen diagram showing a laptop (host) on the left with an arrow pointing to an embedded board (target) on the right, with an SD card in between. Label it: “Cross-compile module install using INSTALL_MOD_PATH”. Alt text: Embedded Linux cross-compilation kernel module install workflow with INSTALL_MOD_PATH.
Step 6 — Generating initramfs and Updating the Bootloader
The Boot Problem initramfs Solves
Here is a situation that comes up on almost every modern Linux machine. The kernel needs to mount the root filesystem to continue booting. But the root filesystem might live on a device that requires a kernel module to work. For example:
The Chicken-and-Egg Boot Problem
❓ What the kernel needs
Root on NVMe SSD → needs nvme.ko
Root on LVM → needs dm-mod.ko
Root on RAID → needs md-mod.ko
Root encrypted → needs dm-crypt.ko
❌ The problem
To load .ko modules you need a filesystem to read them from.
But to read the filesystem you need the modules loaded first.
Classic deadlock.
What initramfs Is
initramfs (initial RAM filesystem) is a small compressed archive — think of it like a tiny ZIP file — that the bootloader loads into RAM alongside the kernel image. The kernel unpacks it into a temporary in-memory filesystem and immediately runs the /init script inside it.
That init script is responsible for loading the early boot modules, setting up the root device, and then handing off to the real filesystem on disk.
What Is Inside a Typical initramfs Archive
initrd (initial ramdisk) was the old approach — a block device image the kernel mounted as a virtual disk. initramfs is the modern replacement — a cpio archive the kernel extracts directly into memory (tmpfs). initramfs is simpler, boots faster, and needs less code in the kernel. Modern distros use initramfs internally even when they still name the file
initrd.img for historical reasons.The Complete Linux Boot Flow
Linux Boot Sequence with initramfs (Linux 6.x)
⚡ Power ON / UEFI Firmware
Initializes CPU and RAM, locates boot device
📄 GRUB2 Bootloader
Reads /boot/grub/grub.cfg → loads vmlinuz-6.6.30 + initrd.img-6.6.30 into RAM
🛠 Kernel Starts
Decompresses itself, sets up memory management, extracts initramfs into tmpfs
💾 /init Script Runs (inside initramfs)
Loads nvme.ko, ext4.ko, etc. → mounts real root at /sysroot → calls switch_root
💻 Real Root Filesystem Active
initramfs RAM freed, /sbin/init (systemd) starts, all services launch
💻 Login Prompt
System fully booted
How to Generate initramfs — By Distribution
On Ubuntu/Debian the easiest method is a single command run from the kernel source directory:
sudo make install
This copies bzImage to /boot/vmlinuz-<version>, calls update-initramfs to build the initramfs, and runs update-grub to refresh the boot menu — all in one step.
On other distributions you may need to do this manually:
# Ubuntu / Debian
sudo update-initramfs -c -k 6.6.30-mykernelbuild
sudo update-grub
# Fedora / RHEL 9 / AlmaLinux / Rocky Linux (uses dracut)
sudo dracut --force /boot/initramfs-6.6.30-mykernelbuild.img 6.6.30-mykernelbuild
sudo grub2-mkconfig -o /boot/grub2/grub.cfg
# Arch Linux / Manjaro (uses mkinitcpio)
sudo mkinitcpio -k 6.6.30-mykernelbuild -g /boot/initramfs-6.6.30-mykernelbuild.img
sudo grub-mkconfig -o /boot/grub/grub.cfg
| Distribution | initramfs Tool | GRUB Update Command |
|---|---|---|
| Ubuntu / Debian | update-initramfs | sudo update-grub |
| Fedora / RHEL 9+ / AlmaLinux | dracut | sudo grub2-mkconfig -o /boot/grub2/grub.cfg |
| Arch / Manjaro | mkinitcpio | sudo grub-mkconfig -o /boot/grub/grub.cfg |
| openSUSE | dracut | sudo grub2-mkconfig -o /boot/grub2/grub.cfg |
Ubuntu 22.04+ and Debian 12+ still use initramfs-tools. Fedora 38+, RHEL 9+, AlmaLinux 9, and Rocky Linux 9 use dracut by default. Arch Linux uses mkinitcpio. All three tools produce the same end result — an initramfs archive in
/boot/.Complete Kernel Build Checklist
End-to-End Kernel Build Steps
make menuconfig — select features and modulesmake -j$(nproc) — compiles vmlinux, bzImage, and all .ko filessudo make modules_install — copies .ko files and runs depmodsudo make install — copies bzImage, builds initramfs, updates GRUBsudo reboot — select new kernel from GRUB menu, confirm with uname -rVerifying After Reboot
# Confirm which kernel is running
uname -r
# Expected: 6.6.30-mykernelbuild
# Confirm module directory exists
ls /lib/modules/$(uname -r)/
# List all currently loaded modules
lsmod
# Check if a specific module is loaded
lsmod | grep ext4
# Load a module manually
sudo modprobe e1000e
# Get details about a module
modinfo e1000e
Screenshot of GRUB boot menu showing two kernel entries — the original distro kernel and the new custom-built kernel. Label it: “GRUB boot menu showing newly installed Linux 6.x kernel”. Alt text: GRUB2 boot menu with custom built Linux kernel entry after make install.
Frequently Asked Questions (FAQ)
What happens if I forget make modules_install before rebooting?
The new kernel will look for modules in /lib/modules/6.6.30-mykernelbuild/ and find nothing there. If the filesystem or disk driver is a module, the system may drop to an emergency shell or fail to boot entirely. Even if it boots, networking, audio, and many devices will not work because their drivers are missing.
Can I run depmod manually if I need to?
Yes. If for any reason you need to regenerate the module dependency files without doing a full install, run: sudo depmod -a 6.6.30-mykernelbuild. The -a flag tells depmod to consider all modules. This is sometimes useful after manually copying extra .ko files into the module directory.
Do I need initramfs if my root filesystem driver is built-in?
Strictly speaking, no — if every driver needed at boot time is compiled directly into the kernel (Y, not M), and you do not use LVM, RAID, or encryption, the kernel can mount the root filesystem directly. But most distribution kernels compile drivers as modules for flexibility, so initramfs becomes necessary. Even if you technically don’t need it, most distros still generate one as a safety net.
How do I add custom modules to the initramfs?
On Ubuntu/Debian, add the module name to /etc/initramfs-tools/modules and run sudo update-initramfs -u -k $(uname -r). On Fedora/RHEL using dracut, add add_drivers+=" mymodule " to a file in /etc/dracut.conf.d/ and regenerate. On Arch with mkinitcpio, add the module to the MODULES array in /etc/mkinitcpio.conf and run sudo mkinitcpio -P.
What is the difference between modprobe and insmod?
insmod loads a single module from a file path you give it. It does not handle dependencies — if a required module is missing, it fails. modprobe reads the modules.dep file and automatically loads all dependencies first, then loads your target module. For all normal use, modprobe is the right tool. insmod is mainly used during kernel module development when you want to load a specific file directly.
What is switch_root and why does the initramfs init script call it?
Once the initramfs init script has loaded necessary modules and mounted the real root filesystem at /sysroot, it calls switch_root /sysroot /sbin/init. This atomically swaps the current root (the tmpfs initramfs) for the real disk root, releases the RAM used by initramfs, and exec-replaces itself with the real init process (systemd on modern systems). After this point the system runs entirely from the real disk.
🎓 Interview Questions & Answers
.ko files in the kernel build tree and copies them to /lib/modules/<version>/kernel/, preserving the subdirectory structure. After the copy it invokes depmod -a <version> which scans all the installed modules, resolves inter-module dependencies, and writes the dependency information into metadata files (modules.dep, modules.alias, modules.symbols). Without this step the kernel and tools like modprobe cannot locate or correctly load any modules at runtime.depmod scans every .ko file in the module directory, reads the symbol imports and exports from each, and figures out which modules depend on which. It writes this to modules.dep (the dependency graph), modules.alias (hardware ID to module name mappings used for auto-loading by udev), and modules.symbols (symbol to module mappings). Together these allow modprobe to load modules and their full dependency chain with a single command.INSTALL_MOD_PATH redirects make modules_install to install kernel modules into a directory you specify instead of the system /lib/modules/. It is essential during cross-compilation for embedded targets. If you are building a kernel for an ARM board on an x86 laptop and run sudo make modules_install without it, the target’s modules get installed into your laptop’s /lib/modules/ — potentially making your laptop unbootable. Setting INSTALL_MOD_PATH to a staging directory keeps the host safe and makes it easy to copy the modules to the target device./boot/grub/grub.cfg and loads the chosen kernel image (vmlinuz) and the initramfs image (initrd.img) into RAM. The kernel decompresses itself, sets up memory management and CPU state, then extracts the initramfs into a tmpfs. The kernel executes /init from the initramfs. That script loads required early-boot modules (NVMe, ext4, dm-crypt, etc.), mounts the real root filesystem, and calls switch_root to atomically swap to the real root and exec systemd. Systemd then brings up all services and eventually presents the login prompt.sudo dracut --force /boot/initramfs-<version>.img <version>. The --force flag overwrites an existing file. The version string must match exactly what uname -r would return for that kernel. After regenerating, run sudo grub2-mkconfig -o /boot/grub2/grub.cfg to update the boot menu.modinfo <module-name> displays metadata embedded in the .ko file: the filename, description, author, license, version, supported kernel version, and most importantly the list of parameters the module accepts. It also shows alias patterns and firmware files the module may need. It is useful for understanding what a module does before loading it, for finding configurable parameters, and for verifying the module was compiled for the correct kernel.
