Building and Installing a Linux Kernel from Source
Grab a release from kernel.org. Recent stable versions (6.x and later) are recommended for production systems:
wget https://www.kernel.org/pub/linux/kernel/v6.x/linux-6.12.tar.gz
tar xf linux-6.12.tar.gz
cd linux-6.12/
If wget isn’t available, use curl:
curl -O https://www.kernel.org/pub/linux/kernel/v6.x/linux-6.12.tar.gz
Verify the tarball
Always verify the kernel’s integrity using checksums and signatures published on kernel.org. This is essential for production systems:
wget https://www.kernel.org/pub/linux/kernel/v6.x/linux-6.12.tar.gz.asc
gpg --verify linux-6.12.tar.gz.asc linux-6.12.tar.gz
You should see output like gpg: Good signature from "Linux Kernel Release Signing Key". If GPG isn’t configured with kernel.org’s public key, import it first:
gpg --keyserver keyserver.ubuntu.com --recv-keys 647F28654894E3BD457199BE38DBBDC86092693E
Install build dependencies
Required packages vary by distribution. On RHEL/CentOS/Rocky/Fedora:
sudo dnf install gcc make ncurses-devel openssl-devel elfutils-devel \
flex bison pahole
On Debian/Ubuntu:
sudo apt-get install build-essential ncurses-dev libssl-dev libelf-dev \
flex bison dwarves
The elfutils-devel/libelf-dev packages handle BTF (BPF Type Format) support, now standard in modern kernels. The pahole/dwarves packages are required for BTF generation during the build. flex and bison are needed for kernel configuration parsing.
Verify your GCC version matches what your distribution uses for its kernel:
gcc --version
For development work or building external kernel modules, also install:
# RHEL/CentOS/Rocky
sudo dnf install kernel-devel
# Debian/Ubuntu
sudo apt-get install linux-headers-$(uname -r)
Prepare a kernel configuration
Compiling a kernel from scratch requires extensive knowledge of hardware and subsystems. Instead, reuse your existing kernel’s configuration as a starting point:
ls /boot/config-*
cp /boot/config-$(uname -r) .config
Update the configuration for your target kernel version:
make oldconfig
This processes your old config against new kernel options, prompting you only for settings that didn’t exist in your previous version. For non-interactive use (accepts defaults for new options):
make olddefconfig
Interactive configuration
If you need to customize options, use a configuration tool. The modern ncurses interface is recommended:
make nconfig
This provides search functionality (press /), which is much faster than navigating through menuconfig on large configurations. You can toggle options with Space (Y for built-in, M for module, N to disable).
Alternative interfaces:
make menuconfig # classic ncurses interface
make xconfig # Qt-based GUI (requires X11)
Both will validate settings and warn about incompatibilities:
.config:444:warning: symbol value 'm' invalid for MICROCODE
.config:618:warning: symbol value 'm' invalid for CPU_FREQ_STAT
These warnings are usually harmless—the build process will automatically disable invalid options.
To review your current configuration without editing:
grep "^CONFIG" .config | head -30
Build and install
Compile using all available CPU cores to significantly reduce build time:
make -j $(nproc)
This spawns one job per CPU core. On a 16-core system, this is dramatically faster than single-threaded compilation.
Install the modules and kernel:
sudo make modules_install
sudo make install
To combine these steps with error handling:
make -j $(nproc) && sudo make modules_install && sudo make install
This runs sequentially and fails fast if any step breaks. Successful completion shows:
INSTALL /lib/firmware/...
DEPMOD 6.12.0
sh ./arch/x86/boot/install.sh 6.12.0 arch/x86/boot/bzImage \
System.map "/boot"
The make install step updates your bootloader automatically on most distributions.
Verify the bootloader entry
For GRUB 2 (RHEL/CentOS/Rocky):
grep -A3 "6.12.0" /boot/grub2/grub.cfg
sudo grub2-mkconfig -o /boot/grub2/grub.cfg
For GRUB 2 (Debian/Ubuntu):
grep -A3 "6.12.0" /boot/grub/grub.cfg
sudo update-grub
For systemd-boot (common on newer installations):
ls /boot/loader/entries/
cat /boot/loader/loader.conf
Check if an entry exists for your new kernel version in /boot/loader/entries/.
Boot the new kernel
Reboot your system:
sudo reboot
During startup, select the new kernel from the GRUB menu, or set it as default:
RHEL/CentOS/Rocky:
sudo grub2-set-default "Rocky Linux (6.12.0) 9"
sudo grub2-mkconfig -o /boot/grub2/grub.cfg
Debian/Ubuntu:
sudo update-grub
systemd-boot:
sudo bootctl set-default linux-6.12.0
After rebooting, verify the running kernel:
uname -r
uname -a
Check that modules loaded correctly:
sudo dmesg | grep -i "modules"
lsmod | head -10
Troubleshooting
Build fails with missing headers
Ensure build tools are installed. RHEL systems may need kernel-devel for in-tree module builds. If you skipped distro-provided kernel headers, some drivers won’t compile. Reinstall dependencies if needed.
Compilation fails partway through
Common causes include missing dependencies or insufficient disk space. A full kernel build can consume 15–20GB in /usr/src. Check available space:
du -sh linux-6.12/
df -h /
If disk space is low, clean previous builds:
make distclean
Then restart the build.
Module signature issues
Newer kernels enforce module signing by default. If external module builds fail with signature errors, either disable signing in .config:
sed -i 's/CONFIG_MODULE_SIG=y/CONFIG_MODULE_SIG=n/' .config
make clean
make -j $(nproc)
Then rebuild. For custom kernels, module signing often causes friction—disabling it is usually the practical choice unless you have specific security requirements (like Secure Boot).
GRUB doesn’t find the new kernel
Manually regenerate the bootloader configuration:
# RHEL/CentOS
sudo grub2-mkconfig -o /boot/grub2/grub.cfg
# Debian/Ubuntu
sudo update-grub
Compilation takes too long
Use make -j $(nproc) to parallelize across all cores. A typical kernel takes 10–30 minutes on modern hardware depending on configuration. To speed things up further, disable unnecessary drivers in nconfig:
- Disable unused hardware support (SCSI, storage controllers you don’t use)
- Remove unneeded filesystems
- Disable network protocols not in use
Each of these can shave several minutes off the build.
Reverting to the old kernel
If the new kernel doesn’t work, reboot and select your previous kernel from GRUB. Once you’ve confirmed the old kernel works, remove the broken one:
sudo rm /boot/vmlinuz-6.12.0 /boot/initramfs-6.12.0.img /boot/System.map-6.12.0
sudo update-grub # or grub2-mkconfig on RHEL/CentOS
The kernel modules remain in /lib/modules/6.12.0/ until you manually delete them:
sudo rm -rf /lib/modules/6.12.0/
