Skip to content

Week 1 — Environment Setup and First Kernel Build

Goal

By the end of this week you will have the kernel source cloned, all build dependencies installed, and your first custom kernel compiled. You won't boot it yet — that's next weeks — but you'll understand what the build produces and how long it takes on your machine.

Why This Matters

Everything in kernel development starts with being able to build the kernel. If you can't build it, you can't test changes, you can't generate patches, you can't contribute. Your 32-core VM is excellent for this — a full kernel build should take under 5 minutes.


Step 1: Install Build Dependencies

The kernel builds with GCC (or Clang), GNU Make, and a handful of tools. On Debian 13:

sudo apt update
sudo apt install -y \
  build-essential \
  bc \
  bison \
  flex \
  libssl-dev \
  libelf-dev \
  libncurses-dev \
  dwarves \
  git \
  fakeroot \
  cpio \
  rsync \
  kmod \
  zstd

What these are:

  • build-essential — GCC, make, libc headers. The compiler toolchain.
  • bc, bison, flex — used by the kernel's build scripts and Kconfig parser.
  • libssl-dev — needed if module signing is enabled (it is by default in most configs).
  • libelf-dev, dwarves — for BTF (BPF Type Format) generation. Modern kernels want this.
  • libncurses-dev — for make menuconfig, the TUI configuration tool.
  • kmod — for modprobe, lsmod, etc.

Step 2: Clone the Kernel Source

You want two remotes: Linus's mainline tree and the netdev tree (the networking subsystem).

cd ~
git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
cd linux

This clones the full history (~4 GB). It will take a while. Once done, add the net-next tree — this is where networking development happens:

git remote add net-next https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next.git
git fetch net-next

There are two networking trees:

  • net — bug fixes for the current release cycle
  • net-next — new features targeting the next merge window

As a new contributor, most of your work will target net-next.

Verify:

git log --oneline -5

You should see recent commits from Linus's tree.

Step 3: Create a Build Configuration

The kernel has thousands of configurable options. You need a .config file that tells the build system what to include. Start with your running kernel's config:

cp /boot/config-$(uname -r) .config

Then update it for the source tree version:

make olddefconfig

This takes your existing config, keeps all your answers, and sets new options to their defaults. It's the safest way to get a working config for a new kernel version.

Expect: A few lines of output showing options being set to defaults.

Step 4: Build the Kernel

make -j$(nproc)

-j$(nproc) runs one job per CPU core. With 32 cores, this is make -j32.

Expect: A wall of output like:

  CC      init/main.o
  CC      init/version.o
  ...
  LD      vmlinux
  ...
  BUILD   arch/x86/boot/bzImage

Build time: On your 32-core VM, roughly 3-8 minutes for the first build. Subsequent builds after small changes take seconds because make only recompiles changed files.

What gets produced:

  • vmlinux — the uncompressed kernel ELF binary (~hundreds of MB). Used for debugging.
  • arch/x86/boot/bzImage — the compressed, bootable kernel image (~10-12 MB). This is what the bootloader loads.
  • *.ko files throughout the tree — kernel modules.

Step 5: Build Modules

make modules -j$(nproc)

This builds all the loadable kernel modules (drivers, filesystems, etc.) that your .config marked as =m (module) rather than =y (built-in).

Step 6: Understand What You Just Built

Run these commands and think about what they tell you:

# Size of the compressed kernel
ls -lh arch/x86/boot/bzImage

# How many modules were built
find . -name '*.ko' | wc -l

# Your kernel version string
make kernelversion

# The full version with local version
cat include/config/kernel.release

Step 7: Set Up Your Editor

Since you use neovim, set up a compile_commands.json for LSP support:

# Install bear if you want compilation database
sudo apt install -y bear

# Generate compile_commands.json
make clean
bear -- make -j$(nproc)

This gives you go-to-definition, autocompletion, and diagnostics in neovim if you have an LSP client configured (clangd works well with the kernel).

Alternatively, the kernel has a built-in script:

python3 scripts/clang-tools/gen_compile_commands.py

Exercises

  1. Run make help and read through the available targets. Note the ones related to configuration (*config targets) and cleaning (clean, mrproper, distclean).
  2. Look at the top-level Makefile. Find where VERSION, PATCHLEVEL, SUBLEVEL are defined. This is how the kernel version string is built.
  3. Run wc -l .config — how many configuration lines does your kernel have?

What's Next

Next week we dive into the configuration system (Kconfig) and menuconfig. You'll learn how to enable/disable features, what =y, =m, =n mean, and how to create a minimal config for QEMU instead of the bloated distro config.