Kernel HOWTO: build and install a Linux kernel today
Building your own Linux kernel takes five commands and about an hour: get the source from kernel.org, start from your distribution's configuration, run make, install it, and reboot. This page walks through each step on current distributions, shows how to check that it worked, and how to go back to your old kernel if it does not.
Old guides told you to run LILO and copy bzImage by hand. Today a distribution script does most of that for you. The kernel build system calls it, and it creates the initramfs (the small startup file system) and adds a boot menu entry.
Do you need your own kernel?
Most people do not. Your distribution's kernel gets security fixes, is signed for Secure Boot, and works with packaged drivers such as NVIDIA's through DKMS. If you build your own, you have to update it yourself whenever a fix comes out. Kernel developers also cannot help with distribution kernels. They ask you to reproduce bugs on a kernel.org kernel.
Good reasons to build one:
- You need a driver or fix that only exists in a newer release, and no packaged newer kernel exists.
- You want to test a patch, or check whether a bug is already fixed upstream before reporting it.
- You are learning how the kernel works, or you need a very small kernel for an embedded board.
If you only need newer hardware support, try a packaged newer kernel first:
- Debian 13: the default kernel is 6.12 (a longterm release). Newer kernels come from trixie-backports (7.2 as of November 2026): add the backports source, then
sudo apt install -t trixie-backports linux-image-amd64. - Ubuntu 24.04 LTS: the HWE (hardware enablement) kernel. Install it with
sudo apt-get install --install-recommends linux-generic-hwe-24.04. Ubuntu 26.04 LTS ships kernel 7.0. - Fedora and Arch already follow recent stable kernels closely.
- 7.3.1latest stable kernel, as of November 2026
- 6.18newest longterm series (projected EOL Dec 2028)
- 9–10 weeksbetween mainline releases
- ~12 GBbuild space with debug info (~4 GB without)
Pick the latest stable release from the front page of kernel.org for everyday use. Mainline release candidates (such as 7.4-rc1) are for testing. Longterm releases get fixes for several years.
Install the build tools
You need GCC 8.1 or newer (or Clang 17 or newer), GNU make 4.0 or newer, binutils, flex, bison, bc, pahole, Perl, and the OpenSSL and libelf development headers. The kernel's own quick-start guide lists these packages:
# Debian, Ubuntu and derivatives
sudo apt install bc binutils bison dwarves flex gcc git make openssl \
pahole perl-base libssl-dev libelf-dev
# Fedora (and RHEL/Alma/Rocky with the same names)
sudo dnf install binutils /usr/include/{libelf.h,openssl/pkcs7.h} \
/usr/bin/{bc,bison,flex,gcc,git,openssl,make,perl,pahole}
# openSUSE
sudo zypper install bc binutils bison dwarves flex gcc git make perl-base \
openssl openssl-devel libelf-devel
For the menu editor make menuconfig, also install the ncurses headers (libncurses-dev on Debian and Ubuntu, ncurses-devel on Fedora).
Work in your home directory as a normal user. You only need root to install. Do not build in /usr/src/linux.
Get the source
Option 1: a release tarball
Download the tarball and its signature from kernel.org. Then check the signature against the uncompressed .tar file. The signature covers the uncompressed tar, so one signature works for every compression format.
cd ~
wget https://cdn.kernel.org/pub/linux/kernel/v7.x/linux-7.3.1.tar.xz
wget https://cdn.kernel.org/pub/linux/kernel/v7.x/linux-7.3.1.tar.sign
gpg --locate-keys torvalds@kernel.org gregkh@kernel.org
unxz linux-7.3.1.tar.xz
gpg --verify linux-7.3.1.tar.sign
tar xf linux-7.3.1.tar
cd linux-7.3.1
Look for "Good signature" from Greg Kroah-Hartman (stable) or Linus Torvalds (mainline). If you see "BAD signature", make sure you checked the .tar and not the .tar.xz.
Option 2: git
A shallow clone of the stable tree downloads only the newest snapshot of one branch:
git clone --depth 1 -b linux-7.3.y \
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git ~/linux
cd ~/linux
Later, git pull fetches new point releases (7.3.2, 7.3.3 and so on).
Configure the kernel
The kernel has thousands of options. Do not start from scratch. Start from the configuration of the kernel you are running now, which you know boots on this machine:
# Debian, Ubuntu, Fedora, RHEL, openSUSE
cp /boot/config-$(uname -r) .config
# Arch and others with CONFIG_IKCONFIG_PROC
zcat /proc/config.gz > .config
# Accept defaults for every option that is new in this release
make olddefconfig
make oldconfig does the same thing but asks you about every new option. That is useful if you want to read each one.
Optional: a smaller, faster build
A distribution configuration builds thousands of modules. To build only the modules that are loaded right now, plug in every device you use (USB disks, webcam, headset, security keys) and run:
yes "" | make localmodconfig
This can cut build time from hours to minutes. The catch: a device you did not plug in before running it will have no driver in the new kernel. That is fine for a test kernel. For your main kernel, keep the full configuration.
Debug information makes the build several times larger. If you do not need it:
./scripts/config --file .config -d DEBUG_INFO \
-d DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT -d DEBUG_INFO_DWARF4 \
-d DEBUG_INFO_DWARF5 -e CONFIG_DEBUG_INFO_NONE
make olddefconfig
Give it a name
Add a tag so your kernel and its modules are installed next to the existing ones instead of over them:
echo "-custom" > localversion
After booting it, uname -r will show something like 7.3.1-custom.
Debian and Ubuntu: fix the certificate setting
Debian's configuration points SYSTEM_TRUSTED_KEYS at a certificate file that is not in the kernel.org source, and the build fails. Clear it:
./scripts/config --file .config --set-str SYSTEM_TRUSTED_KEYS ''
# Ubuntu's configuration also names a revocation list that is not in the source
./scripts/config --file .config --set-str SYSTEM_REVOCATION_KEYS ''
To change other options, run make menuconfig (text menus) or make nconfig. Press / to search for an option by name.
Build it
make -j $(nproc --all)
-j runs one compile job per CPU thread. A trimmed configuration builds in minutes on a modern desktop. A full distribution configuration can take an hour or more. When it finishes you will see a line like Kernel: arch/x86/boot/bzImage is ready. If the build stops, scroll up to the first line that contains error:. Usually a package is missing. For more detail, run make V=1.
Install it
Debian, Ubuntu, Fedora, RHEL and most others
command -v installkernel && sudo make modules_install install
This copies the modules to /lib/modules/7.3.1-custom/. Then make install runs your distribution's installkernel script:
- Debian and Ubuntu: it copies
vmlinuz-7.3.1-custom,System.mapandconfigto/boot. Then it runs the hooks in/etc/kernel/postinst.d/, which create the initramfs withupdate-initramfsand runupdate-grub. - Fedora and RHEL: installkernel passes the kernel to
kernel-install, which builds the initramfs with dracut and adds a boot entry under/boot/loader/entries/.
Arch Linux and others without installkernel
Arch has no installkernel, so you do these steps yourself:
sudo make modules_install
KVER=$(make -s kernelrelease)
sudo install -m 0600 $(make -s image_name) /boot/vmlinuz-$KVER
sudo install -m 0600 System.map /boot/System.map-$KVER
sudo mkinitcpio -k $KVER -g /boot/initramfs-$KVER.img
sudo grub-mkconfig -o /boot/grub/grub.cfg
With systemd-boot instead of GRUB, add a file in /boot/loader/entries/ that points to the new linux and initrd files.
Making the initramfs or boot entry by hand
If a step was skipped or failed, run it again for your version:
sudo update-initramfs -c -k 7.3.1-custom # Debian, Ubuntu
sudo dracut --force --kver 7.3.1-custom # Fedora, RHEL, openSUSE
sudo update-grub # Debian, Ubuntu
sudo grub2-mkconfig -o /boot/grub2/grub.cfg # Fedora, RHEL, openSUSE
Alternative: build a package
The build system can make a real package, which your package manager can later remove cleanly. Use make -j $(nproc) bindeb-pkg for a .deb (the files appear one directory above the source; install them with sudo apt install ../linux-image-*.deb), binrpm-pkg for an RPM, or pacman-pkg for Arch.
Secure Boot
With Secure Boot on, the firmware and shim only start kernels signed with a trusted key. Your kernel is not signed, so it will be refused. Check the state with mokutil --sb-state. You can turn Secure Boot off in the firmware setup while you test. Or you can sign the kernel with your own Machine Owner Key (MOK) and enroll that key with mokutil --import, then confirm it at the next boot. A third option, sudo mokutil --disable-validation, stops shim from checking signatures. It asks for a one-time password, which you confirm in MokManager after a reboot. This switches off signature checking for every boot, not only for your kernel, so turn it back on with sudo mokutil --enable-validation when you have finished testing.
Boot, check and roll back
Reboot. If the GRUB menu is hidden, hold Shift or tap Esc while the machine starts (which key works depends on the firmware and the distribution). Your old kernels are under "Advanced options". After the system starts, check:
uname -r # should print 7.3.1-custom
sudo dmesg --level=err,warn # look for missing firmware or driver errors
systemctl --failed # services that did not start
To try the new kernel only once, so that the next boot goes back to the old one: on Debian and Ubuntu set GRUB_DEFAULT=saved in /etc/default/grub, run sudo update-grub, then sudo grub-reboot "Advanced options for Debian GNU/Linux>Debian GNU/Linux, with Linux 7.3.1-custom". Use the menu titles exactly as your GRUB shows them. On Fedora and RHEL, use sudo grub2-reboot with the entry ID, and sudo grubby --set-default /boot/vmlinuz-<version> to change the default.
If the new kernel does not boot, pick the previous kernel from the menu. Nothing on disk has changed, so the old kernel works as before. To remove the custom kernel, boot one of the other kernels first, and check every version string before you press Enter (never delete the files of the kernel uname -r shows):
sudo rm -rf /lib/modules/7.3.1-custom
command -v kernel-install && sudo kernel-install -v remove 7.3.1-custom
sudo rm -i /boot/{vmlinuz,System.map,config,initrd.img}-7.3.1-custom
sudo update-grub # or: sudo grub2-mkconfig -o /boot/grub2/grub.cfg
If you installed with bindeb-pkg, just run sudo apt remove linux-image-7.3.1-custom instead.
Common mistakes
- "Kernel panic: VFS: Unable to mount root fs": the initramfs is missing, or the driver for your disk or file system is not built. Rebuild the initramfs. With
localmodconfig, check that the NVMe/SATA and file system drivers (ext4, btrfs, dm-crypt for encrypted disks) are enabled. - Overwriting your working kernel: always set a
localversiontag. - Running
make mrproperormake distcleanby mistake: both delete your.config. Keep a copy elsewhere. - Forgetting
O=: if you build in a separate directory withmake O=~/build/kernel, every later make command needs the sameO=. - No more updates: your kernel gets no security fixes unless you rebuild. When 7.3.2 comes out, copy your
.configinto the new tree, runmake olddefconfig, then build and install again. - DKMS modules missing: out-of-tree drivers such as NVIDIA or VirtualBox must be rebuilt for every new kernel, and DKMS needs that kernel's build files to do it. The simplest way is to build with
bindeb-pkg, which also produces alinux-headerspackage.
The classic Kernel HOWTO once kept by the Linux Documentation Project has been withdrawn. The kernel's own documentation at docs.kernel.org is now the reference.
Sources
- How to quickly build a trimmed Linux kernel, kernel.org documentation
- Linux kernel release notes and build instructions (admin-guide/README), kernel.org
- Minimal requirements to compile the kernel, kernel.org
- Kbuild environment variables, kernel.org
- The Linux Kernel Archives: current releases and release types and longterm kernels
- Linux kernel release signatures, kernel.org
- scripts/install.sh (how make install finds installkernel), kernel source
- installkernel(8), Debian
- kernel-install(8), man7.org
- dracut(8), man7.org
- update-initramfs(8), Debian
- mkinitcpio(8), Arch Linux
- grub-reboot(8) and update-grub(8), Debian
- The Linux kernel (grubby, grub2-reboot), Red Hat Enterprise Linux 9 documentation
- Debian Linux Kernel Handbook: common kernel-related tasks
- Debian Backports instructions
- Ubuntu kernel lifecycle and HWE, Canonical
- SecureBoot, Debian Wiki