I find myself fairly regularly hearing about new features in the Linux kernel (the core of Linux operating systems), getting excited, and checking to see if my distribution's repository of packages has the latest kernel built. Unfortunately, Ubuntu's repositories don't have the most recent versions of the kernel available for some time.
Fortunately, the Linux kernel is open source, so we can freely download the kernel's source code, compile it, and install it.
This article details the steps necessary on a fresh install of Ubuntu 19.10 (Eoan Ermine).
Prerequisites
Although we can freely download the code and compile it, we still need some applications installed to download the code and compile the kernel.
We'll need
-
git
, as it allows us to synchronize files from the kernel's codebasebuild-essential
, as it is required to compile the source codekernel-package
, to allow us to build kernel packagesfakeroot
, as it allows us to build Debian packages (which Ubuntu can install)libncurses5
, as it allows us to use nconfig to configure our kernel easilylibssl
, to allow git to pull from secure repositoriesccache
, to cache the build process, which will speed up subsequent buildsflex
andbison
, which are necessary for newer versions to build
All of these can be installed by running:
sudo apt install -y git build-essential kernel-package fakeroot libncurses5-dev libssl-dev ccache flex bison
Note: You may be presented with a Modified configuration file
dialog. If you are, select install the package maintainer's version
, and select Ok
Downloading the Source
We start off by going to the Linux kernel's repository at https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/.
This shows us a bunch of information related to branches of the kernel (kernel development is done on branches for specific things). The master
branch is the main branch for code, and versions of linux are done on separate branches, such as the branch linux-5.4.y
, which holds the source for Linux 5.4.
In this article, I am going to build Linux 5.4, so I'll use this branch.
We start by syncing up with the code in this branch with the command:
git clone -b linux-5.4.y git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
This tells our source control tool (git
) to clone, or copy the code from the linux-5.4.y
branch out of the kernel repository (git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
).
This will take a while to complete, as we have to wait on all of the kernel code to download.
Configuring
We'll start by moving into the code's directory by running `cd linux`.
Now that we have the source code, we need to configure the kernel. We have a few different options for doing this. I'll lay out our options below:
-
- We can use the kernel configuration used to build our current kernel, and select default options for the new features in the kernel we downloaded by running
cp /boot/config-`uname -r` .config
This code copies the configuration for our current kernel, which is located at/boot/config-[kernel version]
, to the source code's directory.
To select all default options, we runyes '' | make oldconfig
.
make oldconfig
updates our old configuration file, and asks for what new options we want to enable.yes ''
just boils down to "hit enter at every option" - We can ignore our current configuration, and just immediately begin configuring.
Note: This is the hardest way to configure, as improper options can lead to the system not booting.
- We can use the kernel configuration used to build our current kernel, and select default options for the new features in the kernel we downloaded by running
Now that we're ready to fire up the configuration option selector, we can run make nconfig
.
nconfig
has every option for your kernel in it. Any option with --->
to the side means that there's a submenu. Use the arrow key on your keyboard to select options, and use the Enter
key to toggle it.
Many options have these options:
-
- A space means that it's not selected to be built
*
means that it will be compiled and loaded when the kernel startsM
means that it will be compiled, but only loaded when needed
Normally, no additional configuration is necessary when you've started the configuration with the old kernel's config.
To save your configuration, press F6
, and F9
to quit.
Compiling
I'm going to cover compiling an installable package, so that it's easy to switch kernels, and so that it can be distributed to others as a .deb
file.
To start the build (which can take hours), run
time KBUILD_BUILD_TIMESTAMP='' sudo make CC="ccache gcc" -j$(nproc --all) deb-pkg LOCALVERSION=-custom
Let's dig into what this command does.
-
time KBUILD_BUILD_TIMESTAMP=''
is necessary, as newer versions of the kernel's source code don't cache properly (which helps future build complete way faster). As a side effect, this also shows the total time that it took to build the kernel.sudo
runs the rest of the command as rootmake
is the build commandCC="ccache gcc"
tellsmake
to use theccache
version ofgcc
, which allows caching-j$(nproc --all)
tells make to use however many processor cores we have on our systemdeb-pkg
says that we want themake
command to make an installable debian packageLOCALVERSION=-custom
tellsmake
that our kernel version should end with-custom
, so that we can know which kernels we have installed are custom builds of the kernel
Now we play the waiting game. Once the build is complete, it'll drop you back to your terminal input.
On my virtual machine, this took 96m83s.
Installing the Kernel
Now that we have a built kernel, we need to install it, make grub wait for us, and reboot into the new kernel.
Your installable kernel packages will likely be different than mine, as different versions, and different kernel suffixes will have different names.
The kernel packages will be located in the parent directory of the kernel's source code. For example, my source code is located at ~/Documents/linux
, so my installable packages are in ~/Documents
.
Here's a list of the packages I got.
linux-headers-5.4.7-custom_5.4.7-custom-1_amd64.deb
linux-image-5.4.7-custom_5.4.7-custom-1_amd64.deb
linux-image-5.4.7-custom_5.4.7-custom-1_amd64.deb
linux-libc-dev_5.4.7-custom-1_amd64.deb
All of these can be installed by running sudo dpkg -i linux-*.deb
After the packages are installed, run sudo vi /etc/default/grub
. This brings you to the vi
editor with the grub settings open. Press i
to get into insert mode, and change the line that says GRUB_TIMEOUT=0
to GRUB_TIMEOUT=10
, or how many seconds you want it to wait. After editing this line, hit ESC
to get to command mode, type in :wq
to write the file and quit, and hit Enter
.
To make these changes apply, run sudo update-grub
.
Now we can restart with sudo reboot
, and when the computer is turning back on, it will show a purple screen. Press Left Shift
to get to the boot menu, choose Advanced options for Ubuntu
, hit Enter
, and you'll see your new kernel, your old one, and recovery mode options.
Once the machine finishes booting, pop open a terminal, and run uname -r
, and you'll see that we're running the kernel we just built.
If it doesn't boot...
If your custom kernel doesn't boot, then it's pretty quick and easy to get back on the old kernel again. Hold Ctrl
+ Alt
+ SysRq
, and while holding those tap R
, then E
, I
, S
, U
, B
, and the computer will restart.
Go back to the advanced options in GRUB again (the purple screen), and select your old kernel (the topmost one that doesn't have -custom
at the end).
You should be back at a working system. Try configuring your kernel again, and adding more drivers, as a failed boot is usually caused by you not including the drivers that your computer needs to run.
Congratulations!
Congratulations on being one of the Linux users who have built the core of their system on their own. Have fun looking into what all of the configuration options are.
If you're looking for a pretty cool customization, look into building a "realtime" kernel. The patches are available at https://mirrors.edge.kernel.org/pub/linux/kernel/projects/rt/, and Google is a great place to find out how to apply them.