Part 2: Kernel Configuration — Getting Your .config Right-Free Linux Kernel Development Course

📚 Linux Kernel Programming Series  |  ← Part 1: The Kbuild System  |  Part 2: Kernel Configuration  |  Part 3: Config Approaches →
Linux Kernel Programming | Part 2

Kernel Configuration

Before a single line of kernel code gets compiled, you need a valid .config. Here is how the kernel figures out where to start.

⏱ ~25 min read
🎯 Beginner → Intermediate
🐧 Kernel 6.x Updated

What you will learn in this part

  • Why a starting .config is critical before you build the kernel
  • How the kernel’s DEFCONFIG_LIST priority system works
  • All the configuration front-ends: menuconfig, xconfig, nconfig, oldconfig
  • What happens when no .config exists at all
  • How to read and understand any .config file
  • The difference between a learning config and a production config

1. The Big Picture — What Has to Happen Before make

You have downloaded the Linux kernel source. You want to build it. Your first instinct might be to just run make. But if you do that without a .config file present in the kernel source root, the build will either fail or pull in a giant default configuration you did not plan for.

The configuration step is not optional — it is the foundation. Think of building a kernel like constructing a building. The .config file is your blueprint. Without it, the builders (compiler and Makefiles) do not know what to build, how big to make it, or which features to include.

Standard Kernel Build Workflow
📥
Download source
kernel.org
→
⚙️
Configure
make menuconfig
YOU ARE HERE
→
🔨
Compile
make -j$(nproc)
→
📦
Install
make modules_install
→
🚀
Boot
update grub, reboot

2. The DEFCONFIG_LIST — How Kbuild Finds a Starting Config

What happens if you type make menuconfig in the kernel source directory but there is no .config file yet? The kernel build system does not crash — it uses a clever fallback priority list to find a starting configuration.

This priority list is defined in init/Kconfig inside a special entry called DEFCONFIG_LIST. Here is what it looks like in a modern 6.x kernel:

# init/Kconfig (Linux 6.x — simplified)

config DEFCONFIG_LIST
    string
    depends on !UML
    option defconfig_list
    default "/lib/modules/$(shell,uname -r)/.config"
    default "/etc/kernel-config"
    default "/boot/config-$(shell,uname -r)"
    default ARCH_DEFCONFIG
    default "arch/$(ARCH)/defconfig"

The Kbuild system walks through this list from top to bottom and stops at the first match it finds. Let us understand each entry:

DEFCONFIG_LIST — Priority Order (Highest First)
1
/lib/modules/$(uname -r)/.config

The config file from your currently running kernel’s module directory. Most common on desktop/server Linux systems.

HIGHEST
2
/etc/kernel-config

A system-wide kernel config file. Rarely present, but allows administrators to place a standard config in a fixed location.

FALLBACK
3
/boot/config-$(uname -r)

The config your Linux distribution ships with alongside its pre-built kernel. This is very commonly found on Ubuntu, Fedora, Debian systems.

FALLBACK
4
ARCH_DEFCONFIG

An architecture-specific default. Each CPU architecture (x86, ARM, RISC-V, etc.) has a minimal working default defined in its own Kconfig.

FALLBACK
5
arch/$(ARCH)/defconfig

A built-in minimal defconfig file inside the kernel source tree for the current architecture. The last resort — always exists.

LAST RESORT
⚠️ Critical Override Rule: If a .config file already exists in the kernel source root directory, the entire DEFCONFIG_LIST is ignored. The existing .config always takes priority over everything. This means once you have set up your config, re-running make menuconfig will always start from your existing choices.

Check What Config Files Are Available on Your System

Before you start configuring, it is useful to know what is already available on your machine:

# Check your running kernel version
uname -r

# See if /lib/modules config exists (priority 1)
ls /lib/modules/$(uname -r)/.config 2>/dev/null && echo "Found!" || echo "Not present"

# See if /boot config exists (priority 3 — most common on Ubuntu/Debian/Fedora)
ls /boot/config-$(uname -r) 2>/dev/null && echo "Found!" || echo "Not present"

# On Ubuntu/Debian, your running kernel config is often available here too
zcat /proc/config.gz 2>/dev/null | head -5  # Only if CONFIG_IKCONFIG_PROC=y
Tip: On most Ubuntu systems, you will find /boot/config-$(uname -r) already present. This is the configuration that Canonical used to build your current running kernel. It is an excellent starting point when you want to build a custom kernel for your own machine.

3. The Configuration Front-Ends

The kernel provides several ways to interact with the configuration. Under the hood, they all do the same thing — read the Kconfig files and write to your .config file. The difference is the user interface.

make menuconfig

Text-based menu interface that runs in the terminal. Works over SSH, no GUI required. The most widely used and recommended option.

Requires: libncurses-dev
Run: make menuconfig
make nconfig

A newer, improved terminal interface with colour themes and function-key navigation. Similar to menuconfig but with a better look and feel.

Requires: libncurses-dev
Run: make nconfig
make xconfig

A Qt-based graphical interface. Useful on a desktop system when you want a mouse-driven GUI with a search bar and tree view of all options.

Requires: qt5-default / qtbase5-dev
Run: make xconfig
make gconfig

A GTK-based graphical interface. Similar to xconfig but uses the GTK toolkit instead of Qt. Less commonly used today.

Requires: libgtk2.0-dev
Run: make gconfig

Automation-Friendly Config Targets

Beyond the interactive UIs, there are several non-interactive config targets that are extremely useful in scripted or CI/CD environments:

make target What it does
make defconfig Creates a minimal default .config for your current architecture. Good starting point on embedded targets.
make oldconfig Takes an existing .config and asks you about only the new options added since that config was created. Very useful when upgrading kernel versions.
make olddefconfig Like oldconfig but automatically sets all new options to their default values — no prompts. Very handy in automated builds.
make localmodconfig Scans currently loaded kernel modules and creates a minimal config containing only those. Produces a lean kernel tailored for your current system.
make listnewconfig Lists all new config options that appear in the new kernel but are missing from your current .config. Useful before running oldconfig.

4. Anatomy of a .config File

Let us take a detailed look at a real .config snippet and understand every part of it:

#
# Automatically generated file; DO NOT EDIT.
# Linux/x86_64 6.12.0 Kernel Configuration
#                                            ← Header comment — never edit above this

CONFIG_CC_VERSION_TEXT="gcc (Ubuntu 13.2.0)"  ← Compiler info (auto-detected)
CONFIG_CLANG_VERSION=0
CONFIG_64BIT=y                               ← 64-bit kernel (y = built-in)
CONFIG_X86_64=y                              ← Target is x86_64 architecture

#
# General setup                             ← Section comment — these are just labels
#
CONFIG_LOCALVERSION=""                       ← Empty = no suffix on kernel version
CONFIG_LOCALVERSION_AUTO=y                   ← Auto-append git hash to version

# CONFIG_SWAP is not set                    ← This feature is DISABLED (n)
CONFIG_SYSVIPC=y                             ← System V IPC is enabled, built-in

#
# Kernel features
#
CONFIG_HZ_250=y                              ← Select 250 HZ timer option
CONFIG_HZ=250                               ← Numeric config = 250 ticks/sec
CONFIG_PREEMPTION=y                          ← Preemptible kernel enabled

#
# Loadable module support
#
CONFIG_MODULES=y                             ← Module support enabled
CONFIG_MODULE_UNLOAD=y                       ← Allow unloading modules
CONFIG_MODULE_FORCE_UNLOAD=y                 ← Force unload even if in use

#
# Networking
#
CONFIG_NET=y                                 ← Core networking stack: built-in
CONFIG_PACKET=y                              ← Raw packet sockets: built-in
CONFIG_BT=m                                 ← Bluetooth: built as a module
# CONFIG_WIRELESS is not set               ← WiFi: disabled completely

🔍 Three patterns you will see in every .config

CONFIG_FOO=y
Feature is enabled and compiled directly into the kernel image (built-in)
CONFIG_FOO=m
Feature is compiled as a separate loadable kernel module (.ko file)
# CONFIG_FOO is not set
Feature is disabled. This comment format is intentional — it lets grep and scripts detect that this option was seen and explicitly disabled

5. Learning Config vs Production Config — An Important Distinction

This is something that many tutorials skip over, but it is really important to understand before you start experimenting.

🧪 Learning / Experimenting
  • Use any starting config you like
  • Run on a virtual machine or spare hardware
  • Try different options to see what breaks
  • Compile the whole thing including debug symbols
  • Build failures are learning opportunities
  • No real consequence if the kernel does not boot
🏭 Production System
  • Always start from a known working config
  • Test on identical hardware before deploying
  • Make one change at a time and verify
  • Keep your .config in version control
  • Keep a fallback/rescue kernel always available
  • Breakage can affect real users and real systems
Real-world caution: The Linux kernel has thousands of configuration options. Some combinations are valid but produce a kernel that does not boot on your hardware. Others may boot but cause silent data corruption or instability under load. This is why the golden rule for production is: start from a proven config, make the smallest change you need, test thoroughly, and keep your old kernel as a fallback.

6. How to Find and Identify Config Options

When you are configuring a kernel, you often need to find a specific option. Here are the practical ways to do that:

Using the Search Feature in menuconfig

Inside make menuconfig, press the / key to open a search dialog. Type the feature name or CONFIG symbol you are looking for:

Press / in menuconfig, then type:

  BT                  → Finds CONFIG_BT (Bluetooth)
  EXT4                → Finds CONFIG_EXT4_FS
  USB_SERIAL          → Finds USB serial port driver option

The search results show:
  - The full config name (CONFIG_BT)
  - Its current value (y/m/n)
  - Where it is located in the menu tree
  - Which options it depends on
  - Which options it selects automatically

Using grep on the .config File

# Check if Bluetooth is enabled and how
grep CONFIG_BT /boot/config-$(uname -r)
# Output: CONFIG_BT=m

# Find all USB-related options
grep CONFIG_USB /boot/config-$(uname -r) | head -20

# Find all options currently set to 'm' (modules)
grep "=m$" /boot/config-$(uname -r) | wc -l
# Typical output on Ubuntu: 4000+ modules!

Using scripts/config — The Scriptable Way

The kernel source includes a helper script at scripts/config that lets you read and set config values from the command line — very useful for automated builds:

# Check the current value of an option
scripts/config --state CONFIG_BT
# Output: m

# Enable an option as built-in
scripts/config --enable CONFIG_BT

# Set an option as a module
scripts/config --module CONFIG_BT

# Disable an option completely
scripts/config --disable CONFIG_WIRELESS

# Set a string value
scripts/config --set-str CONFIG_LOCALVERSION "-mykernel"

# After using scripts/config, run this to clean up any dependency issues:
make olddefconfig

Part 2 — Quick Recap

  • A .config file must exist before the build can start
  • Kbuild uses DEFCONFIG_LIST to find a starting config when no .config is present — it walks through five fallback locations in order
  • If a .config already exists in the kernel source root, it overrides everything in DEFCONFIG_LIST
  • Multiple front-ends exist: menuconfig (most popular), nconfig, xconfig, gconfig
  • Non-interactive targets like olddefconfig and localmodconfig are great for automation
  • For production, always start from a known-good, tested config and keep it in version control

Interview Questions

Q1. What happens if you run make menuconfig on a fresh kernel source with no existing .config?
Kbuild reads the DEFCONFIG_LIST from init/Kconfig and checks each location in priority order. It first looks for a .config in /lib/modules/$(uname -r)/, then /etc/kernel-config, then /boot/config-$(uname -r), then the architecture’s ARCH_DEFCONFIG, and finally the built-in arch/$(ARCH)/defconfig. The first file found becomes the seed config, and menuconfig starts from there. If a .config already exists in the kernel source root, all of this is bypassed.
Q2. What is the difference between make oldconfig and make olddefconfig?
Both commands are used when you have an existing .config from an older kernel and want to update it for a newer kernel that has new configuration options. make oldconfig prompts you interactively for each new option, asking what value you want. make olddefconfig is non-interactive — it automatically sets all new options to their default values without prompting. olddefconfig is preferred in automated build pipelines and CI/CD systems.
Q3. Why does the comment “# CONFIG_FOO is not set” appear instead of simply CONFIG_FOO=n in .config?
This is intentional and has practical value. If a line simply said CONFIG_FOO=n, a grep for CONFIG_FOO might match other options like CONFIG_FOOBAR depending on the search. More importantly, the comment format # CONFIG_FOO is not set serves as a clear signal to tools and scripts that this option was evaluated and explicitly set to no — it was not accidentally omitted. Some tools use this comment pattern to distinguish between “disabled” and “not yet evaluated.”
Q4. How can you find out what config your currently running kernel was built with?
There are two common ways. First, check /boot/config-$(uname -r) — most Linux distributions store a copy of the kernel’s .config here alongside the kernel image. Second, if the kernel was built with CONFIG_IKCONFIG=y and CONFIG_IKCONFIG_PROC=y, you can read the config directly from the running kernel using zcat /proc/config.gz. The first method works on most distro kernels; the second requires that those options were enabled at build time.
Q5. What is the scripts/config utility and when would you use it?
scripts/config is a shell script included in the kernel source tree that lets you read and modify individual options in a .config file from the command line without launching an interactive UI. It supports operations like –enable, –disable, –module, –set-str, and –state. It is primarily used in automated build systems, CI pipelines, and shell scripts where you need to enable or disable specific options programmatically. After using it, always run make olddefconfig to resolve any dependency issues that may have been introduced.

2 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *