Kernel HOWTO: build and install a Linux kernel today

Updated November 2026

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:

If you only need newer hardware support, try a packaged newer kernel first:

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

make modules_install make install → installkernel initramfs (initramfs-tools/dracut) boot entry (GRUB / BLS)
Modules go in first, because the initramfs is built from them. The distribution's installkernel script does the last two steps.

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:

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

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