Compiling and Installing a Custom Kernel
Compiling a custom kernel is the deepest level of Linux. This post covers the full process — getting the source, configuring with menuconfig, building, generating an initramfs, updating GRUB, and booting your own kernel.
Series: Learning Linux from Scratch
- 1. Learning Linux from Scratch — After a Full IT Apprenticeship
- 2. What is Linux?
- 3. The Filesystem
- 4. Users and Permissions
- 5. Installing and Managing Software
- 6. Text Editors
- 7. Shell Scripting Basics
- 8. Process Management
- 9. Networking Fundamentals
- 10. SSH
- 11. systemd and Services
- 12. Disk Management
- 13. Users and Groups — In Depth
- 14. Cron and Scheduled Tasks
- 15. Firewall — iptables and ufw
- 16. Environment Variables and the Shell
- 17. Log Management
- 18. Kernel Module Management
- 19. The /proc Filesystem — In Depth
- 20. The /sys Filesystem and udev
- 21. Kernel Parameters and sysctl
- 22. Compiling and Installing a Custom Kernel
Compiling a custom kernel is the deepest level of Linux. Most people never need to do it — distribution kernels are well-configured and heavily tested. But knowing how gives you a complete picture of what the kernel actually is, lets you enable features not in distribution kernels, apply patches, and strip down the kernel for embedded or specialised systems.
This post covers the full process from source to boot.
Why compile a custom kernel?
- Enable features disabled in the distribution kernel
- Apply a patch not yet merged upstream (realtime patch, hardware support, security fix)
- Strip unused drivers for a smaller, faster kernel (embedded systems, VMs)
- Learn how the kernel configuration works
- Test a newer kernel version before it is packaged
Prerequisites
Install the build dependencies:
# Debian/Ubuntu
sudo apt install build-essential libncurses-dev bison flex libssl-dev libelf-dev bc dwarves
# Fedora/RHEL
sudo dnf install gcc make ncurses-devel bison flex openssl-devel elfutils-libelf-devel bc dwarvesYou will need at least 20GB of free disk space and expect the build to take 30 minutes to several hours depending on your CPU.
Getting the kernel source
From kernel.org
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.6.30.tar.xz
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.6.30.tar.sign
xz -d linux-6.6.30.tar.xz
tar xf linux-6.6.30.tar
cd linux-6.6.30Verify the signature
gpg --locate-keys torvalds@kernel.org gregkh@kernel.org
gpg --verify linux-6.6.30.tar.signFrom your distribution
Distributions also ship kernel source packages. On Debian/Ubuntu:
sudo apt install linux-source
tar xf /usr/src/linux-source-*.tar.bz2This gives you the distribution kernel source with all their patches applied.
Kernel configuration
The kernel has thousands of options. The configuration is stored in a file called .config in the source directory.
Starting from your current config
The best starting point is your running kernel's config:
cp /boot/config-$(uname -r) .config
make olddefconfigolddefconfig sets any new options that did not exist in the old config to their defaults. This gives you a config close to what you are already running.
make menuconfig
The interactive configuration tool:
make menuconfigThis opens a terminal UI where you browse the full option tree. Navigate with arrow keys, Enter to expand sections, space to toggle options.
Each option can be:
[*]— built into the kernel (always available)[M]— built as a loadable module[ ]— not compiled at all
Important sections:
- General setup — kernel compression, init system, cgroup support
- Processor type and features — CPU family, number of CPUs, preemption model
- Device Drivers — everything hardware related
- Networking support — protocols, netfilter, wireless
- File systems — which filesystems to support
- Security options — SELinux, AppArmor, seccomp
make nconfig and make xconfig
Alternatives to menuconfig:
make nconfig— similar but with a slightly different interfacemake xconfig— graphical Qt-based configurator (requires Qt dev packages)
Reducing the config for a VM or specific hardware
For a minimal kernel, start from scratch:
make localmodconfigThis reads the currently loaded modules (lsmod) and builds a config that only includes what is currently in use. Results in a much smaller kernel but only safe if all your hardware is active at the time you run it.
Building the kernel
Once configured, build it:
make -j$(nproc)-j$(nproc) uses all available CPU cores. This is the step that takes time. On a modern machine with 8 cores, expect 20–45 minutes.
Build output:
arch/x86/boot/bzImage— the compressed kernel imagevmlinux— the uncompressed kernel (for debugging)System.map— symbol table
Building and installing modules
sudo make modules_installThis copies compiled modules to /lib/modules/$(kernel-version)/.
Installing the kernel
sudo make installOn most systems this copies the kernel image and System.map to /boot/ and updates the bootloader automatically via update-grub or equivalent.
Manually, the files that need to go to /boot/:
sudo cp arch/x86/boot/bzImage /boot/vmlinuz-6.6.30-custom
sudo cp System.map /boot/System.map-6.6.30-custom
sudo cp .config /boot/config-6.6.30-customGenerating the initramfs
The initramfs is a minimal filesystem loaded into memory at boot before the real root filesystem is mounted. It contains the modules needed to mount root.
sudo update-initramfs -c -k 6.6.30-customOr with dracut (RHEL/Fedora):
sudo dracut --force /boot/initramfs-6.6.30-custom.img 6.6.30-customUpdating the bootloader
GRUB
sudo update-grubThis scans /boot/ for kernels and regenerates /boot/grub/grub.cfg. Your new kernel will appear in the GRUB menu on the next boot.
To set the new kernel as default, edit /etc/default/grub:
GRUB_DEFAULT=0
0 boots the first entry (most recent kernel). Then apply:
sudo update-grubBooting with GRUB menu visible
If GRUB normally skips the menu, hold Shift during boot to show it. Or set in /etc/default/grub:
GRUB_TIMEOUT=5
GRUB_TIMEOUT_STYLE=menu
Verifying the new kernel
After rebooting into your custom kernel:
uname -rYou should see your custom version string. If you set CONFIG_LOCALVERSION in menuconfig, that suffix appears here.
cat /proc/versionIf the kernel does not boot
Always keep the previous working kernel available in GRUB. If the new kernel does not boot, select the old one from the GRUB menu and boot back in.
Common issues:
- Missing modules for the root filesystem — kernel cannot mount root
- Missing initramfs — always generate one after installing
- Wrong root device in bootloader config
- Unsupported CPU features enabled in config
Check kernel boot messages with:
journalctl -b -1 # logs from the previous boot
dmesg | head -100Cleaning up
After a successful build, remove intermediate build files to free space:
make clean # remove compiled objects but keep config
make mrproper # remove everything including configTo remove an old kernel:
sudo rm /boot/vmlinuz-6.6.30-custom
sudo rm /boot/initrd.img-6.6.30-custom
sudo rm /boot/System.map-6.6.30-custom
sudo rm /boot/config-6.6.30-custom
sudo rm -rf /lib/modules/6.6.30-custom
sudo update-grubCompiling your own kernel is not something you do every day. But doing it once teaches you more about how Linux actually works than almost anything else. The configuration tool alone — tens of thousands of options, each documented — is a complete education in what the kernel does.
The series has now covered Linux from the filesystem all the way down to building the kernel itself. That is the full stack.