A quickstart guide to building Linux for the first time.

Daunting at first, building Linux yourself is rewarding, useful, & highly educational. In this post, I'll outline everything you need to build & test your own kernel from a Debian-based operating system, like Ubuntu. Most of what I'm sharing here I learned from trying to build my own pet distro.

Step 0: Ready your compiler & host tooling

Before we even think about downloading a source archive, we need to ready our host system with the tools necessary to compile Linux. The commands outlined in this section are Debian/Ubuntu specific, so if you use another distro or operating system, you will need to work out a different set of incantations.

First, lets install a minimal toolchain containing a C / C++ compiler, and debian package tooling.

sudo apt-get install build-essential

So now we have a C compiler, but we still need to install the remaining tooling. Fortunately for us, debian-based distributions track the dependencies required to build each package against each package, so if we can figure out how to install all the packages in the build dependencies list for the linux package, we are done. Huzzah!

Apt-get again comes to the rescue and makes this easy:

sudo apt-get build-dep linux

Boom! Done.

Step 1: Download & unpack Linux source

The kernel folk keep sourcecode for all the different versions of Linux neatly packed at, so go ahead and pick a reasonably-recent version. For the sake of this post, we will use 5.4.20, with a direct download here.

Its good practice to check the hash of your downloaded file to make sure its authentic, even if it was delivered over HTTPS. From checking other sources the .tar.xz archive SHA256 is 793f6dcd9a7074dc61bbb092b40b3ce9567f776f21589ecd09f07bc9ed5c67bb, so we can check it like this:

[xxx@xxx]:/tmp> sha256sum linux-5.4.20.tar.xz
793f6dcd9a7074dc61bbb092b40b3ce9567f776f21589ecd09f07bc9ed5c67bb  linux-5.4.20.tar.xz
Looks good to me!

Lastly, lets unpack it into its own directory structure. For the sake of this post, lets unpack & build it in /tmp/linux-5.4.20.

mkdir /tmp/linux-5.4.20
tar -xf linux-5.4.20.tar.xz linux-5.4.20 --directory /tmp/linux-5.4.20
cd /tmp/linux-5.4.20
  • Directory and file names are version-dependent - make sure you update the commands if you are using a different version of Linux.
  • We referenced the directory linux-5.4.20 after the archive filename linux-5.4.20.tar.xz. We did this because the archive contains the folder linux-5.4.20, and we want to extract its contents. If we didn't specify linux-5.4.20, the source code would have been extracted to /tmp/linux-5.4.20/linux-5.4.20.

Step 2: Configure Linux how you like it

As you can imagine, theres heaps of options you can tweak to configure how Linux operates and what pieces are included. These options are expressed in the Kconfig file, which is located at .config once generated (If you extracted Linux to the same directory as in step 1, thats /tmp/linux-5.4.20/.config).

Before I tell you how to build this file, know this: Its easy to get overwhelmed about the myriad of options available to you and what they mean. Don't worry too much, just pick a starting point, and update your config + rebuild Linux as you need to change things.

Establishing a Kconfig file for the first time

The easiest way to get started is to generate your Kconfig based on the current system. Run this:

make olddefconfig

This command will take the current Kconfig of your running system, and use that. New options will be set to their default values. The generated file will be written to .config.

If you run it a second time (specifically, if a .config file already exists), it will use the existing configuration, but set any missing options to their default values.

Tweaking your Kconfig

Linux ships with a nice, menu-based configurator, which you can easily invoke like this:

make menuconfig

You'll be able to browse around at your own leisure using the keyboard, manually investigating, enabling, and disabling options as you please. You can enable a highlighted option by pressing y, disable an option with n, and (if supported, symbolized by angle brackets preceeding the option) compile it as a module using m.

Setting an option as a module (ie: pressing m) means enabling that option, but compiling it into its own file (which can be loaded only if its needed) rather than bloating the kernel. If you want to enable something and it supports being set as a module, enable it as a module (rather than pressing y) unless theres a good reason.

Scripting changes to Kconfig

Resist the temptation to manually edit .config, unless you are sure of what you are doing. This is because some options depend on others or are incompatible with other options. Instead, use provided tooling to script changes to Kconfig, and then run make olddefconfig:

Enable (ie: y) an option
scripts/config --enable CONFIG_MODULE_UNLOAD
Disable (ie: n) an option
scripts/config --disable CONFIG_MODULE_UNLOAD
Enable (ie: m) an option as a module
scripts/config --module CONFIG_INTEL_MEI_ME

If you were wondering, make olddefconfig runs through your configuration to make sure its consistent, and for any missing options, sets them to defaults.

Step 3: Build Linux

All the hard work is done! The last thing you need to do is kick off the build, and go get coffee while you wait.

Run this:

make -j 8 deb-pkg # Replace 8 with the number of cores your system has
Running deb-pkg not only builds Linux, but packages it neatly into a debian package we can copy, or install. We could have just run make if we didn't want it neatly packaged.

Step 4: Install Linux

Thanks to the deb-pkg recipe, our newfangled kernel is now packaged neatly into three debian packages, one directory below our source directory:

[xxx@xxx]:/tmp/linux-5.4.20> ls -lah ../*.deb
-rw-r--r-- 1 xxx xxx  11M Feb 17 16:58 ../linux-headers-5.4.20_5.4.20-1_amd64.deb
-rw-r--r-- 1 xxx xxx 8.9M Feb 17 16:58 ../linux-image-5.4.20_5.4.20-1_amd64.deb
-rw-r--r-- 1 xxx xxx 1.1M Feb 17 16:58 ../linux-libc-dev_5.4.20-1_amd64.deb

In most cases you only need to install the linux-image-KERNEL_VERSION package, like this:

sudo dpkg -i ../linux-image-5.4.20_5.4.20-1_amd64.deb
However, If you expect to develop userland or kernel code, you probably want to install all 3: sudo dpkg -i ../linux-*.deb.

Thats it! dpkg will take care of adjusting your boot filesystem, initrd, and grub configuration to make your new kernel available. Enjoy your customized kernel!