Compile Linux Kernel: 9 Easy Steps and 5 Errors Fixed

To compile Linux kernel source on Debian or Ubuntu: install build-essential flex bison bc libssl-dev libelf-dev, download and verify the tarball from kernel.org, copy your running config with cp /boot/config-$(uname -r) .config, run make olddefconfig, build with make -j$(nproc), then sudo make modules_install and sudo make install and reboot. This guide walks through every step with real output, including the four build errors you get when a package is missing and the certificate error that stops most Ubuntu builds.

Every command used to compile Linux kernel 7.2.12 below was run on 2026-10-23 in a Debian 12 (bookworm) KVM virtual machine with 12 vCPUs and 5 GiB RAM, gcc 12.2.0 and binutils 2.40, building Linux 7.2.12, the latest stable release on kernel.org that day. The VM started on the distro kernel 6.1.0-47-amd64. All output is copied from two lab runs.

What you need before you compile Linux kernel source

The minimal tool versions are listed in the kernel's own Minimal requirements to compile the Kernel page: for Linux 7.2 that is GNU C 8.1, GNU make 4.0, flex 2.5.35, bison, openssl and libcrypto 1.0.0 and others. Debian 12's gcc 12 is far above that. Plan for disk space too: the unpacked 7.2.12 source was 1.8 GB, and after a trimmed build the tree was 5.6 GB.

A fresh Debian 12 server has none of the helpers you need to compile Linux kernel source:

# dpkg -l flex bison bc libssl-dev libelf-dev dwarves libncurses-dev
dpkg-query: no packages found matching bc
dpkg-query: no packages found matching libssl-dev
dpkg-query: no packages found matching libelf-dev
...
un  bison          <none>       <none>       (no description available)
un  flex           <none>       <none>       (no description available)

Install everything at once to compile Linux kernel source without the errors shown later. libncurses-dev is only needed for make menuconfig; debhelper libdw-dev rsync only for building .deb packages:

sudo apt install build-essential flex bison bc libssl-dev libelf-dev wget xz-utils gpg
sudo apt install libncurses-dev                 # optional: make menuconfig
sudo apt install debhelper libdw-dev rsync      # optional: make bindeb-pkg

Step 1: download and verify the source before you compile Linux kernel code

Get the tarball and its signature from kernel.org. The .tar.sign file signs the uncompressed tar, so you pipe it through xz -cd. On Debian the command is gpg; there is no gpg2 binary (the first lab run failed with gpg2: command not found):

$ wget -q https://cdn.kernel.org/pub/linux/kernel/v7.x/linux-7.2.12.tar.xz https://cdn.kernel.org/pub/linux/kernel/v7.x/linux-7.2.12.tar.sign
$ ls -l linux-7.2.12.tar.*
-rw-r--r-- 1 vagrant vagrant       989 Oct 21 08:54 linux-7.2.12.tar.sign
-rw-r--r-- 1 vagrant vagrant 160445268 Oct 21 08:54 linux-7.2.12.tar.xz

$ gpg --locate-keys torvalds@kernel.org gregkh@kernel.org
gpg: key 38DBBDC86092693E: public key "Greg Kroah-Hartman <gregkh@kernel.org>" imported
gpg: key 79BE3E4300411886: public key "Linus Torvalds <torvalds@kernel.org>" imported

$ xz -cd linux-7.2.12.tar.xz | gpg --verify linux-7.2.12.tar.sign -
gpg: Signature made Wed Oct 21 08:34:21 2026 UTC
gpg:                using RSA key 647F28654894E3BD457199BE38DBBDC86092693E
gpg: Good signature from "Greg Kroah-Hartman <gregkh@kernel.org>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
Primary key fingerprint: 647F 2865 4894 E3BD 4571  99BE 38DB BDC8 6092 693E

"Good signature" is what matters; the WARNING only means you have not personally signed Greg's key. Compare the fingerprint with the list on the kernel.org signature page. To see what a damaged download looks like, the lab changed one byte in a copy of the tarball:

gpg: BAD signature from "Greg Kroah-Hartman <gregkh@kernel.org>" [unknown]

Then unpack it and check the version in the Makefile before you compile Linux kernel code from it:

$ tar -xf linux-7.2.12.tar.xz && du -sh linux-7.2.12 && head -5 linux-7.2.12/Makefile
1.8G	linux-7.2.12
# SPDX-License-Identifier: GPL-2.0
VERSION = 7
PATCHLEVEL = 2
SUBLEVEL = 12

Step 2: configure the kernel

The easiest way to compile Linux kernel builds that boot on your machine is to start from the config of the kernel you are running, so all your hardware keeps working. Without flex the very first make target fails:

$ cp /boot/config-$(uname -r) .config && make olddefconfig
  LEX     scripts/kconfig/lexer.lex.c
/bin/sh: 1: flex: not found
make[2]: *** [scripts/Makefile.host:9: scripts/kconfig/lexer.lex.c] Error 127

After installing flex and bison, make listnewconfig shows how many options the 6.1 config does not know yet. make oldconfig would ask you about each one; make olddefconfig answers all of them with the default:

$ make listnewconfig | wc -l
936
$ make olddefconfig
#
# configuration written to .config
#
$ grep -c '=m' .config
3723

A config with 3,723 modules takes a long time to build. If you compile Linux kernel code only for the machine you are on, make localmodconfig keeps just the modules that are loaded right now (61 lines of lsmod in this VM). Do not use it on a laptop with unplugged USB devices: their drivers are dropped.

$ yes '' | make localmodconfig
...
# configuration written to .config
$ grep -c '=m' .config
63

Two more changes: a version suffix so the new kernel cannot overwrite the distro one, and no debug info, which makes the build much bigger and slower:

$ scripts/config --set-str LOCALVERSION -lab --disable DEBUG_INFO_DWARF5 --enable DEBUG_INFO_NONE
$ make olddefconfig
$ grep -E '^CONFIG_LOCALVERSION=' .config
CONFIG_LOCALVERSION="-lab"

Use make menuconfig instead if you want to pick options by hand.

Step 3: compile Linux kernel and fix the errors you will hit

To compile Linux kernel sources, the lab ran make -j$(nproc) and installed only what each error asked for. Attempt 1 failed after 4 seconds with two missing headers:

scripts/sign-file.c:25:10: fatal error: openssl/opensslv.h: No such file or directory
/home/vagrant/linux-7.2.12/tools/objtool/include/objtool/elf.h:10:10: fatal error: gelf.h: No such file or directory

gelf.h comes from libelf-dev and openssl/opensslv.h from libssl-dev. With both installed, attempt 3 stopped at the next missing tool:

/bin/sh: 1: bc: not found
make[2]: *** [Kbuild:24: include/generated/timeconst.h] Error 127

After apt install bc, attempt 4 finished:

$ time make -j$(nproc)    # attempt 4
[exit 0 after 279 s]
  BUILD   arch/x86/boot/bzImage
Kernel: arch/x86/boot/bzImage is ready  (#1)

$ ls -lh arch/x86/boot/bzImage; make -s kernelrelease
-rw-r--r-- 1 vagrant vagrant 8.5M Oct 22 22:34 arch/x86/boot/bzImage
7.2.12-lab

The time to compile Linux kernel code here, 279 seconds on 12 vCPUs, is for the trimmed localmodconfig build. The full distro config has 3,723 modules instead of 63, so expect a much longer build without it.

compile linux kernel 7.2.12 on Debian 12: bc not found error, then bzImage is ready
Lab run: the last missing package, then a successful build.

Compile Linux kernel on Ubuntu: No rule to make target 'debian/canonical-certs.pem'

Debian 12's config does not set a trusted key file, so make olddefconfig wrote CONFIG_SYSTEM_TRUSTED_KEYS="" and no extra step was needed. Ubuntu is different. The config in the Ubuntu 24.04 package linux-headers-6.8.0-147-generic contains:

CONFIG_SYSTEM_TRUSTED_KEYS="debian/canonical-certs.pem"
CONFIG_SYSTEM_REVOCATION_KEYS="debian/canonical-revoked-certs.pem"

Those files exist only in Ubuntu's own kernel source. The lab set the same values in the Debian tree and got the error Ubuntu users report:

make[3]: *** No rule to make target 'debian/canonical-certs.pem', needed by 'certs/x509_certificate_list'.  Stop.
make[2]: *** [scripts/Makefile.build:550: certs] Error 2

The fix is to clear both options. After make olddefconfig the disabled option becomes an empty string, and the build ran through:

$ scripts/config --disable SYSTEM_TRUSTED_KEYS --disable SYSTEM_REVOCATION_KEYS
$ grep -E 'SYSTEM_(TRUSTED|REVOCATION)_KEYS' .config
# CONFIG_SYSTEM_TRUSTED_KEYS is not set
# CONFIG_SYSTEM_REVOCATION_KEYS is not set
$ make olddefconfig && grep -E 'SYSTEM_(TRUSTED|REVOCATION)_KEYS' .config
CONFIG_SYSTEM_TRUSTED_KEYS=""

Step 4: install the kernel you just compiled and reboot

make modules_install copies the modules to /lib/modules/7.2.12-lab (159 MB here, modules are signed with the key generated during the build). make install copies the kernel to /boot, builds the initramfs and updates GRUB through the hooks in /etc/kernel/postinst.d:

$ sudo make modules_install
  SIGN    /lib/modules/7.2.12-lab/kernel/virt/lib/irqbypass.ko
  DEPMOD  /lib/modules/7.2.12-lab
$ sudo make install
  INSTALL /boot
run-parts: executing /etc/kernel/postinst.d/initramfs-tools 7.2.12-lab /boot/vmlinuz-7.2.12-lab
update-initramfs: Generating /boot/initrd.img-7.2.12-lab
run-parts: executing /etc/kernel/postinst.d/zz-update-grub 7.2.12-lab /boot/vmlinuz-7.2.12-lab
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-7.2.12-lab
Found linux image: /boot/vmlinuz-6.1.0-47-amd64
done

With GRUB_DEFAULT=0 (the Debian default) the highest version boots first, and the old kernel stays under "Advanced options". After sudo reboot:

$ uname -r
7.2.12-lab
$ uname -v
#1 SMP PREEMPT_DYNAMIC Thu Oct 22 22:34:32 UTC 2026
$ systemctl is-system-running
running

dmesg --level=err,warn showed only notes about old boot parameters from the VM image (elevator= and scsi_mod.use_blk_mq have no effect on new kernels), nothing from missing drivers.

Alternative: compile Linux kernel as a .deb package

If you compile Linux kernel builds often on Debian or Ubuntu, a package is easier to remove later. Without the packaging tools, make bindeb-pkg stops right away:

dpkg-checkbuilddeps: error: Unmet build dependencies: debhelper-compat (= 12) libdw-dev:native rsync
make[2]: *** [scripts/Makefile.package:126: bindeb-pkg] Error 3

With debhelper libdw-dev rsync installed, a clean tree built in 199 seconds and produced four packages:

$ ls -lh /home/vagrant/*.deb
-rw-r--r-- 1 vagrant vagrant 9.4M Oct 22 22:44 /home/vagrant/linux-headers-7.2.12-lab_7.2.12-1_amd64.deb
-rw-r--r-- 1 vagrant vagrant 114M Oct 22 22:44 /home/vagrant/linux-image-7.2.12-lab-dbg_7.2.12-1_amd64.deb
-rw-r--r-- 1 vagrant vagrant  12M Oct 22 22:44 /home/vagrant/linux-image-7.2.12-lab_7.2.12-1_amd64.deb
-rw-r--r-- 1 vagrant vagrant 1.5M Oct 22 22:44 /home/vagrant/linux-libc-dev_7.2.12-1_amd64.deb

$ sudo dpkg -i linux-image-7.2.12-lab_7.2.12-1_amd64.deb
Setting up linux-image-7.2.12-lab (7.2.12-1) ...
update-initramfs: Generating /boot/initrd.img-7.2.12-lab
Generating grub configuration file ...
done

Boot the old kernel and remove the new one

Every time you compile Linux kernel versions yourself, keep a way back. To boot the old one once without changing the default, use grub-reboot with the menu path from grub.cfg:

$ sudo grub-reboot 'Advanced options for Debian GNU/Linux>Debian GNU/Linux, with Linux 6.1.0-47-amd64'
$ sudo reboot
$ uname -r
6.1.0-47-amd64

A packaged kernel is removed with apt, which also deletes the initramfs and updates GRUB:

$ sudo apt-get purge linux-image-7.2.12-lab
Removing linux-image-7.2.12-lab (7.2.12-1) ...
update-initramfs: Deleting /boot/initrd.img-7.2.12-lab
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-6.1.0-47-amd64
done

A kernel installed with make install has no package, so you delete its files by hand: /boot/vmlinuz-7.2.12-lab, initrd.img-, System.map-, config- and /lib/modules/7.2.12-lab, then run sudo update-grub. On RHEL, CentOS and Fedora see how to remove old kernels.

Summary

To compile Linux kernel 7.2 on Debian 12 you need flex, bison, bc, libssl-dev and libelf-dev on top of build-essential; each missing one gives a clear error that names the file or tool. Verify the tarball with gpg, start from your running config, trim it with localmodconfig for a fast first build, clear the Ubuntu certificate options if you build on Ubuntu, and keep the distro kernel in GRUB so you can always boot back. For the kernel's own short version of this process, read How to quickly build a trimmed Linux kernel; to see how containers share this kernel, see Docker and the Linux kernel.

Comments

Popular posts from this blog

How to Play Minecraft Bedrock Edition on Linux: A Comprehensive Guide for Tech Professionals

The Ultimate Guide: How to Set Up DXVK in Wine on Linux for Enhanced Gaming Performance

Linux desktop frozen? My 5 go-to tricks to try - before forcing a hard reboot