Building the Linux Kernel
Building the kernel yourself lets you enable experimental features, apply custom patches (like PREEMPT_RT), shrink the kernel for embedded systems, or simply understand how it all fits together. The process takes about 30 minutes on modern hardware.
Getting the Source
# Official source: kernel.org
# Download latest stable tarball:
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.12.tar.xz
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.12.tar.sign
# Verify GPG signature (optional but good practice):
xz -d linux-6.12.tar.xz
gpg --verify linux-6.12.tar.sign linux-6.12.tar
# Extract:
tar xf linux-6.12.tar
cd linux-6.12
# Or clone with git (full history, ~4GB):
git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
cd linux
# Stable branch (production-ready point releases):
git clone https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
git checkout linux-6.12.y
Build Dependencies
# Ubuntu/Debian:
apt install build-essential libncurses-dev bison flex libssl-dev \
libelf-dev dwarves bc make gcc pkg-config
# Fedora/RHEL:
dnf install gcc make ncurses-devel bison flex openssl-devel \
elfutils-libelf-devel dwarves bc
# Verify gcc works:
gcc --version
make --version
# Optional: ccache speeds up repeated builds
apt install ccache
export CC="ccache gcc"
Building the Kernel
# Build everything (kernel + all modules):
# -j$(nproc) uses all CPU cores in parallel
make -j$(nproc)
# Build output:
# arch/x86/boot/bzImage ← compressed kernel image
# *.ko files throughout ← kernel modules
# Build time estimates (on modern 8-core machine):
# make localmodconfig build: ~5-10 minutes
# make olddefconfig build: ~20-40 minutes
# Full allmodconfig build: ~2+ hours
# Watch build with verbosity (debug build failures):
make -j$(nproc) V=1 2>&1 | tee build.log
# Build just a specific module (fast):
make -j$(nproc) drivers/net/ethernet/intel/e1000/e1000.ko
# Cross-compile for ARM64 (e.g., building for Raspberry Pi on x86):
apt install gcc-aarch64-linux-gnu
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
make -j$(nproc)
Installing the Kernel
# Install kernel modules to /lib/modules/VERSION/:
sudo make modules_install
# Install kernel image + update initramfs + update GRUB:
sudo make install
# This runs installkernel script which:
# 1. Copies bzImage to /boot/vmlinuz-VERSION
# 2. Copies System.map to /boot/System.map-VERSION
# 3. Copies .config to /boot/config-VERSION
# 4. Generates initrd/initramfs: update-initramfs -c -k VERSION
# 5. Updates bootloader: update-grub
# Verify files are in /boot:
ls /boot/ | grep $(make kernelversion)
# config-6.12.0-custom
# initrd.img-6.12.0-custom
# System.map-6.12.0-custom
# vmlinuz-6.12.0-custom
# GRUB should now list the new kernel:
grep menuentry /boot/grub/grub.cfg | head -5
# Reboot and select new kernel from GRUB menu:
sudo reboot
# After reboot — verify:
uname -r
# 6.12.0-custom
Cleanup and Removing a Kernel
# Remove a kernel you no longer want:
# 1. Remove from /boot:
sudo rm /boot/vmlinuz-6.12.0-custom
sudo rm /boot/initrd.img-6.12.0-custom
sudo rm /boot/System.map-6.12.0-custom
sudo rm /boot/config-6.12.0-custom
# 2. Remove modules:
sudo rm -rf /lib/modules/6.12.0-custom
# 3. Update GRUB:
sudo update-grub
# Clean build artifacts (before switching configs):
make clean # removes built files, keeps .config
make mrproper # removes everything including .config
make distclean # removes even editor backup files
# Check build size:
du -sh vmlinux # uncompressed kernel (~100-300MB)
du -sh arch/x86/boot/bzImage # compressed image (~10-15MB)