Table of Contents
Why (and When) You’d Compile Your Own Kernel
Compiling a custom kernel is about tailoring the Linux kernel to your exact needs instead of using a generic distribution kernel. Common reasons:
- Hardware enablement: support for very new hardware not yet in your distro kernel.
- Performance: removing unneeded subsystems, using better CPU tuning, or enabling specific schedulers/options.
- Features: enabling experimental features, special filesystems, or security options.
- Debugging/Development: adding debug options, custom patches, or instrumentation.
For most users, the distribution kernel is sufficient; compiling your own makes sense when you explicitly need something it doesn’t provide.
High-Level Workflow
Compiling a custom kernel on a typical distribution generally follows this pattern:
- Prepare the system (install build tools and kernel headers).
- Get kernel sources (from your distro or kernel.org).
- Start from an existing configuration (usually the distro’s config).
- Configure the kernel for your needs.
- Build the kernel and modules.
- Install modules and kernel image.
- Update bootloader (often automatic via distro tools).
- Boot and test the new kernel.
- Keep the old kernel as a fallback.
The details differ slightly between distributions, but the core steps are similar.
Preparing Your Build Environment
You should build on the same architecture and (ideally) same distribution you plan to run the kernel on.
Basic Requirements
You’ll typically need:
- A C toolchain:
gcc,binutils,make. - Development tools:
flex,bison,bc,openssldevelopment headers,libelfheaders,perl,python3,tar,xz, etc. - Sufficient disk space: 10–20 GB free is comfortable for full kernel builds.
- RAM: at least 4 GB; more helps compilation speed.
- A working known-good kernel installed as a fallback.
Installing build dependencies (examples)
On Debian/Ubuntu:
sudo apt update
sudo apt install build-essential libncurses-dev bison flex libssl-dev \
libelf-dev bc dwarvesOn Fedora:
sudo dnf groupinstall "Development Tools"
sudo dnf install ncurses-devel bison flex elfutils-libelf-devel openssl-devel \
bc dwarvesOn Arch Linux:
sudo pacman -S base-devel ncurses bc flex bison openssl zstd
Check the kernel documentation (Documentation/process/changes.rst in the source tree) for an up‑to‑date list of required tools.
Obtaining Kernel Sources
You have two main options:
1. Distro-Provided Kernel Source
Pros: integrates better with your distribution’s tooling, patches, and configuration.
Examples:
- Debian/Ubuntu:
sudo apt install linux-source
cd /usr/src
tar xf linux-source-*.tar.xz
cd linux-source-*- Fedora:
sudo dnf install kernel-devel kernel-src
# Sources commonly under /usr/src/kernels or /usr/src- Arch Linux:
You can use the linux package’s PKGBUILD or asp to fetch source and patch set.
2. Upstream Kernel from kernel.org
Pros: latest mainline or LTS kernels, unmodified by distros.
cd ~/src
mkdir -p kernel && cd kernel
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.*/linux-6.*.tar.xz
tar xf linux-6.*.tar.xz
cd linux-6.*/Choose an LTS kernel for stability unless you specifically need mainline.
Starting from an Existing Configuration
Using your distribution’s existing kernel configuration is the safest starting point. It tends to include all necessary drivers for your hardware and distro specifics.
Common ways to get the current config:
/boot/config-$(uname -r)(most distros)/proc/config.gz(if enabled; you may need tozcatit)
Example:
cp /boot/config-$(uname -r) .configThis imports your current kernel’s configuration into the new source tree.
To sync your .config with any new options introduced by a newer kernel:
make oldconfigThis will prompt you for options that didn’t exist in the old kernel; you can usually accept the defaults unless you specifically need something.
Kernel Configuration Interfaces
All configuration interfaces produce/update the .config file in the kernel source directory; they just give you different ways to edit it.
Text-Based Menu: `menuconfig`
Requires libncurses dev libraries.
make menuconfigYou get a hierarchical menu. Each item can be:
[*]built into the kernel (=y)[M]built as a module (=m)[ ]disabled (=n)
Relevant options for custom builds:
General setup → Local version - append to kernel release: add a suffix like-customso your new kernel version appears as6.6.1-custom.Processor type and features: choose specific CPU model optimizations (e.g.,Core 2,Generic x86-64, etc.).Device Drivers: enable/disable hardware support, filesystems, networking drivers, etc.Kernel hacking: debugging options (stack traces, debug symbols, sanitizers; useful in development, but can bloat and slow the kernel).
Graphical Interfaces: `xconfig` and `gconfig`
If you have a graphical environment:
make xconfig # Qt-based
# or
make gconfig # GTK-basedThese provide a GUI to navigate options. They’re convenient when you’re unfamiliar with option names.
Noninteractive Updates: `oldconfig` and `defconfig`
make oldconfig: reuse and update an existing.config, prompting only for new options.make olddefconfig: reuse.configand apply default answers to new options (no interactive prompts).
This is useful when doing minor version bumps with minimal changes.
Key Configuration Decisions
You can easily break booting if you disable the wrong thing. Focus on:
Built-In vs Module
- Built-in (
y): driver is always available; mandatory for things required to mount the root filesystem (disk drivers, filesystem of/, key bus drivers). - Module (
m): can be loaded on-demand; good for non-critical hardware (USB peripherals, rarely used filesystems, etc.). - Disabled (
n): not present at all.
A safe rule: if you’re not sure whether something is required for boot, keep the distro’s choice (usually y or m) and don’t switch it to n.
CPU and Optimization
Under Processor type and features:
- Choose the right Processor family if you are targeting a specific machine; this may improve performance slightly.
- Enable features your CPU supports (like virtualization, if you use it) and disable ones that are definitely impossible on your CPU (if you know what you’re doing).
Filesystems and Storage
Ensure support for:
- The filesystem used for your root (
/) partition (e.g.,EXT4,XFS,Btrfs) is built-in (=y), not just a module (unless you use an initramfs capable of loading it). - The drivers for your disk controller / NVMe / SATA are enabled and typically built-in.
Network and Drivers
If you rely on network-booting or remote access for administration, ensure relevant drivers and options remain enabled.
Building the Kernel
Once the .config is ready, you can compile.
Parallel Build
Use all CPU cores for speed (e.g., -j$(nproc)):
make -j"$(nproc)"This builds:
- The kernel image (e.g.,
vmlinux,bzImage). - The kernel modules.
Build times depend on hardware and config: from minutes to an hour+.
Building Only Modules (Optional)
To recompile modules after config changes:
make modules -j"$(nproc)"
But normally you just run plain make, which includes modules.
Installing Modules and Kernel Image
Installing Modules
As root:
sudo make modules_install
This installs compiled modules into /lib/modules/<kernel-version>/.
Installing the Kernel and Updating Bootloader
Most distros recognize make install and perform the correct steps:
sudo make installTypically, this will:
- Copy the kernel image to
/boot(e.g.,/boot/vmlinuz-<version>). - Copy
System.mapand sometimesconfig. - Run distro-specific hooks to:
- Generate or update the initramfs if your system uses one.
- Update the bootloader configuration (GRUB, etc.)
On Debian/Ubuntu-style systems that prefer packages, an alternative is to use make deb-pkg (see below) and install .deb packages.
Creating and Using an initramfs
Many systems rely on an initramfs to provide drivers and scripts needed during early boot (e.g., encrypted disks, LVM). If your distro scripts don’t automatically build one for your custom kernel, you may have to:
- On Debian/Ubuntu:
sudo update-initramfs -c -k $(make kernelrelease)- On Fedora and many others (dracut-based):
sudo dracut -f /boot/initramfs-$(make kernelrelease).img $(make kernelrelease)The exact command depends on your distribution. Ensure the initramfs filename matches what your bootloader entry expects.
Using Distro-Style Kernel Packages
Instead of bare make install, you can build native packages, which integrates better with your package manager and simplifies removal.
Debian/Ubuntu: `make deb-pkg`
From the kernel source tree:
make -j"$(nproc)"
make deb-pkg -j"$(nproc)"
This creates .deb packages in the parent directory (e.g., ../linux-image-...deb, ../linux-headers-...deb). Install them with:
cd ..
sudo dpkg -i linux-image-*.deb linux-headers-*.debThe install scripts will automatically:
- Place the kernel in
/boot. - Generate an initramfs.
- Update GRUB.
RPM-Based: `make rpm-pkg`
On Fedora/RHEL-like systems:
make rpm-pkg -j"$(nproc)"
This produces RPMs (often in ~/rpmbuild/RPMS/), which you can install with dnf or rpm -i.
Managing Multiple Kernels Safely
You almost never want to replace your distro kernel completely. Instead, install your custom kernel alongside it.
Best practices:
- Give the custom kernel a unique version suffix (e.g.,
6.6.1-custom) inGeneral setup → Local version. - Ensure at least one known-good distro kernel remains installed.
- Verify that the bootloader menu lists multiple kernels; GRUB typically keeps several.
If boot fails with the new kernel, reboot and select the old kernel from the bootloader menu.
Bootloader Entries and Selection
After installing the new kernel:
- Check
/bootfor files like: vmlinuz-6.6.1-custominitrd.img-6.6.1-custom(or similar)- Check that the bootloader configuration was updated:
- GRUB:
/boot/grub/grub.cfg(generated from/etc/grub.dand/etc/default/grub). - Some systems use
grub2-mkconfigduring install.
If necessary, regenerate GRUB config manually (example on Debian/Ubuntu):
sudo update-grubOr on some RPM-based systems:
sudo grub2-mkconfig -o /boot/grub2/grub.cfgAt the next boot, choose your custom kernel from the GRUB menu if it isn’t the default.
Verifying the New Kernel
Once booted, confirm you are running the intended kernel:
uname -rYou should see the version (including any local suffix) you configured.
Verify that:
- Hardware is working (network, storage, graphics, etc.).
- Modules are loaded as expected:
lsmod- Logs show no critical errors during boot (
journalctl -bfor systemd-based systems).
Common Pitfalls and How to Avoid Them
Missing Root Filesystem or Disk Driver
Symptom: kernel panic at boot, complaining it can’t mount root.
Causes:
- The disk controller driver (e.g., SATA, NVMe, RAID) or filesystem driver (e.g.,
EXT4,XFS,Btrfs) is built as a module but not included in initramfs, or disabled entirely.
Mitigation:
- Ensure these are built-in (
=y). - Or ensure initramfs is correctly generated and contains necessary modules.
Over-Aggressive Stripping of Options
Symptom: some hardware or features stop working unexpectedly.
Cause: disabling subsystems based on limited understanding of their purpose.
Mitigation:
- Start with your distro config and make incremental changes.
- After each round of changes, rebuild and test before removing more.
Enabling Too Many Debug Options
Symptom: sluggish performance, large kernel image, full logs.
Cause: Kernel hacking options like kernel debugging, lock debugging, sanitizers, extra checks.
Mitigation:
- Keep only debug options you really need.
- Use a separate debug kernel for troubleshooting and a lean one for production.
Slow Rebuilds After Minor Changes
You don’t need to make clean for every small change; incremental builds track what changed.
For more drastic config changes or patching, consider:
make clean
# or more thorough:
make mrproper
make mrproper removes .config, so back it up first if needed.
Tips for Efficient Kernel Development and Experimentation
- Keep your
.configunder version control (git initin your kernel source dir) so you can track what you changed. - Name your kernels clearly using the
Local versionstring, e.g.,-debug,-perf,-laptop. - Use ccache to speed up repeated builds:
sudo apt install ccache # or equivalent on your distro
export CC="ccache gcc"
make -j"$(nproc)"- Separate build directory (out-of-tree builds) using
O=to keep your source tree clean:
mkdir -p ../build-kernel
make O=../build-kernel oldconfig
make O=../build-kernel -j"$(nproc)"
sudo make O=../build-kernel modules_install
sudo make O=../build-kernel installThis is especially useful when building multiple kernels from the same source.
Cleaning Up and Reverting
If you decide to revert to only the distro kernel:
- Remove custom kernel packages via your package manager (if you used
deb-pkg/rpm-pkg). - Or manually:
- Remove custom kernel images and initramfs from
/boot(carefully). - Remove the corresponding
/lib/modules/<custom-version>directory. - Regenerate bootloader config if needed.
Always verify that a working kernel remains present in /boot and in your bootloader menu before deleting anything.
When to Avoid a Custom Kernel
Compiling your own kernel isn’t always appropriate, especially on:
- Production servers where stability and vendor support matter more than minor optimizations.
- Systems under a support contract that requires vendor kernels.
- Beginner setups where debugging a broken boot would be difficult.
In those cases, prefer vendor kernels or vendor-provided kernels with backported features, and use custom kernels only in lab/test environments.