Loading content...
A Linux kernel can be many things: a minimal 2MB image for an embedded device, a fully-featured 15MB distribution kernel supporting thousands of hardware configurations, or a finely-tuned server kernel optimized for a specific workload. The difference lies entirely in configuration.
Kernel configuration determines:
This page takes you from configuration novice to power user, capable of building kernels tailored to any purpose.
By the end of this page, you will understand: (1) The Kconfig configuration system architecture, (2) Multiple configuration interfaces and when to use each, (3) Key configuration categories and their impacts, (4) How to start from existing configurations, (5) Configuration management best practices, and (6) Advanced configuration techniques for specific use cases.
Linux uses a custom configuration system called Kconfig, developed specifically for the kernel's needs. Unlike typical build configuration (autoconf, cmake), Kconfig is designed for:
Kconfig Files:
Every subsystem directory contains a Kconfig file defining its configuration options. These are hierarchically included from the root Kconfig.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
## USB device configuration# menuconfig USB_SUPPORT bool "USB support" depends on HAS_IOMEM default y help This option adds core support for Universal Serial Bus (USB). You will also need drivers for each USB peripheral you want. If unsure, say Y. if USB_SUPPORT config USB tristate "Support for Host-side USB" depends on USB_ARCH_HAS_HCD select USB_COMMON select NLS # for langid strings help Universal Serial Bus (USB) is a specification for a serial bus with hot-plugging capability. Common USB devices include keyboards, mice, modems, printers, scanners, and more. To compile this as a module, choose M. The module is called usbcore. config USB_ANNOUNCE_NEW_DEVICES bool "USB verbose debug messages" depends on USB default n help Say Y here if you want the USB core & hub driver to produce verbose messages about every new device connected to your system. config USB_XHCI_HCD tristate "xHCI HCD (USB 3.0) support" depends on USB && HAS_DMA select USB_XHCI_PLATFORM if USB_XHCI_HCD=y help The eXtensible Host Controller Interface (xHCI) is the standard USB 3.0 host controller. If your system has a USB 3.0 port, you'll need this. To compile as a module, choose M. The module is called xhci-hcd. choice prompt "Default USB Storage method" depends on USB_STORAGE default USB_STORAGE_ASYNC config USB_STORAGE_ASYNC bool "Asynchronous SCSI scanning" help Scan for USB storage devices asynchronously. config USB_STORAGE_SYNC bool "Synchronous SCSI scanning" help Scan for USB storage devices synchronously (slower boot).endchoice endif # USB_SUPPORTKconfig Syntax Elements:
| Keyword | Description |
|---|---|
config | Defines a configuration symbol (creates CONFIG_SYMBOL in .config) |
menuconfig | Like config, but also acts as a menu container |
bool | Binary option (y/n) |
tristate | Three-state option (y/m/n) |
int/hex/string | Typed values |
depends on | Prerequisite conditions |
select | Force-enables another option when this is enabled |
default | Default value |
help | Multi-line help text |
choice/endchoice | Mutually exclusive options |
menu/endmenu | Organizational grouping |
if/endif | Conditional inclusion |
The Dependency System:
Kconfig's dependency handling prevents invalid configurations. If you enable CONFIG_USB_XHCI_HCD, Kconfig automatically:
CONFIG_USB is enabled (dependency)CONFIG_USB_XHCI_PLATFORM if built-in (select)CONFIG_HAS_DMA is met (hardware capability)The select keyword force-enables dependencies without checking their dependencies! This can create broken configurations. Kernel developers are advised to use select sparingly and prefer depends on when possible. If you see build errors about missing symbols, a broken select chain may be the cause.
The kernel provides multiple interfaces for configuration, each suited to different workflows:
| Command | Interface | Best For | Requirements |
|---|---|---|---|
make config | Line-by-line text prompts | Scripted builds (responds to stdin) | None |
make menuconfig | ncurses TUI (terminal UI) | Interactive exploration | libncurses-dev |
make nconfig | Newer ncurses TUI | Similar to menuconfig, different style | libncurses-dev |
make xconfig | Qt graphical UI | GUI with search, easy navigation | qt5-default |
make gconfig | GTK graphical UI | GNOME-style GUI | libgtk-3-dev |
make oldconfig | Prompt only for new options | Updating existing configs | Existing .config |
make olddefconfig | Like oldconfig, use defaults for new | Automated config updates | Existing .config |
make defconfig | Create default config for arch | Starting point for customization | None |
make savedefconfig | Save minimal config | Version-controlled configs | Existing .config |
make allmodconfig | Maximum modules | Distribution-style builds | None |
make localyesconfig | Built-in for loaded modules only | Minimal custom kernel | Running system |
Using menuconfig:
The most common interface for interactive configuration is make menuconfig:
$ make menuconfig
This launches a terminal-based menu system with the following navigation:
| Key | Action |
|---|---|
| ↑/↓ | Navigate menu items |
| Enter | Enter submenu or toggle option |
| Space | Toggle option (y/m/n cycle) |
| Y | Force enable (built-in) |
| M | Set to module |
| N | Disable |
| / | Search for symbol |
| ? | Show help for current item |
| Esc Esc | Go back/exit |
| S | Save |
| L | Load alternate config |
The Search Feature:
Press / and type to search. Results show:
While menuconfig is traditional, make xconfig offers superior usability: persistent context (shows all options, not just current menu), easier search with clickable results, and split-pane showing configuration and help simultaneously. Well worth installing Qt dependencies for regular kernel work.
The output of kernel configuration is the .config file in the kernel source root. This human-readable text file contains all configuration decisions:
#
# Automatically generated file; DO NOT EDIT.
# Linux/x86 6.6.0 Kernel Configuration
#
#
# General setup
#
CONFIG_LOCALVERSION="-custom"
CONFIG_LOCALVERSION_AUTO=y
CONFIG_BUILD_SALT=""
CONFIG_DEFAULT_HOSTNAME="(none)"
CONFIG_SWAP=y
CONFIG_SYSVIPC=y
CONFIG_SYSVIPC_SYSCTL=y
CONFIG_POSIX_MQUEUE=y
...
# CONFIG_MODULES is not set <- Feature disabled
...
CONFIG_USB=m <- Module
CONFIG_USB_XHCI_HCD=m
Key Points:
# are comments or disabled optionsCONFIG_SYMBOL=y means built into kernel imageCONFIG_SYMBOL=m means compiled as loadable module# CONFIG_SYMBOL is not set means explicitly disabledAuto-generated Files:
After running configuration, several files are generated:
| File | Purpose |
|---|---|
.config | Master configuration file |
include/generated/autoconf.h | C header with #define CONFIG_* |
include/config/auto.conf | Makefile-includable version |
include/config/auto.conf.cmd | Dependency tracking |
include/config/tristate.conf | Module vs built-in tracking |
1234567891011121314151617181920212223242526272829303132333435
# View current config$ head -50 .config # Count configuration options$ grep -c '^CONFIG_' .config6847 # Find a specific option$ grep CONFIG_USB .config | head -5CONFIG_USB_SUPPORT=yCONFIG_USB_COMMON=mCONFIG_USB=mCONFIG_USB_XHCI_HCD=mCONFIG_USB_XHCI_PCI=m # Check if a feature is enabled$ grep CONFIG_BTRFS_FS .configCONFIG_BTRFS_FS=mCONFIG_BTRFS_FS_POSIX_ACL=yCONFIG_BTRFS_FS_CHECK_INTEGRITY=y # Programmatically modify config (using scripts/config)$ ./scripts/config --enable CONFIG_DEBUG_INFO$ ./scripts/config --disable CONFIG_MODULES$ ./scripts/config --module CONFIG_EXT4_FS$ ./scripts/config --set-val CONFIG_LOG_BUF_SHIFT 18$ ./scripts/config --set-str CONFIG_LOCALVERSION "-debug" # Show diff between two configs$ diff -u old.config new.config$ scripts/diffconfig old.config .config # Validate configuration$ make olddefconfig # Apply defaults to new options$ make listnewconfig # Show new options needing valuesThe scripts/config script allows scriptable .config manipulation. This is essential for CI/CD pipelines, automated testing, and programmatic kernel builds. It's safer than text manipulation because it understands Kconfig syntax.
Building a kernel from scratch (all 18,000+ options) is impractical. Instead, start from a reasonable baseline and customize.
/boot/config-$(uname -r) from your running system. Maximizes hardware compatibility. Run make olddefconfig to update for new kernel version.make defconfig creates a reasonable default for your architecture. Good for embedded or when starting fresh. Relatively minimal.make localmodconfig builds modules only for hardware currently loaded on your system. Creates a fast, minimal kernel for your exact hardware.arch/*/configs/. Example: make bcm2711_defconfig for Raspberry Pi 4.make tinyconfig for absolute minimal (won't boot!), make allnoconfig (disable everything), make allyesconfig (enable everything built-in).1234567891011121314151617181920212223242526272829
# Strategy 1: Start from running kernel's config$ cp /boot/config-$(uname -r) .config$ make olddefconfig # Update for new kernel version$ make menuconfig # Customize # Strategy 2: Build only for current hardware (minimal kernel)$ lsmod > /tmp/loaded_modules.txt # Save current modules$ make localmodconfig # Enable only loaded modules$ make menuconfig # Add anything missing # Strategy 3: Start from arch defaults$ make defconfig # Creates x86-appropriate defaults$ make menuconfig # Heavy customization needed # Strategy 4: Use vendor defconfig (embedded)$ ls arch/arm64/configs/bcm2711_defconfig defconfig ...$ make bcm2711_defconfig # Raspberry Pi 4$ make menuconfig # Strategy 5: Create minimal config for saving# After configuring, save a minimal version (only non-defaults)$ make savedefconfig$ mv defconfig my_custom_defconfig# This creates a much smaller file suitable for version control # Later, restore from saved minimal config$ cp my_custom_defconfig arch/x86/configs/$ make my_custom_defconfigWith localmodconfig, any hardware not currently connected/loaded will be omitted. If you occasionally use USB audio, external graphics, or other removable hardware, manually enable those drivers. It's easy to create a kernel that boots but can't use your WiFi because the adapter wasn't plugged in during config!
With thousands of options, knowing which categories matter most is essential. Here are the high-impact areas:
Processor Type and Features:
| Option | Impact | Recommendation |
|---|---|---|
CONFIG_SMP | Multi-processor support | Enable unless single-core embedded |
CONFIG_NR_CPUS | Max CPU count | Set to actual max (default 8192 is overkill) |
CONFIG_PREEMPT_* | Preemption model | PREEMPT for desktop, PREEMPT_NONE for throughput |
CONFIG_HZ | Timer frequency | 1000 for desktop, 250/100 for server |
CONFIG_NO_HZ_* | Tickless operation | FULL for servers (power saving) |
CONFIG_CPU_FREQ | Frequency scaling | Enable for power management |
CONFIG_X86_64 | 64-bit mode | Almost always yes |
Preemption Models:
PREEMPT_NONE: Never preempt kernel code. Maximum throughput, worst latency.PREEMPT_VOLUNTARY: Preempt at explicit yield points. Balanced.PREEMPT: Preempt anywhere except spinlocks. Best interactivity, some overhead.The kconfig-hardened-check tool (https://github.com/a13xp0p0v/kconfig-hardened-check) analyzes your .config against security best practices. Run it before deploying production kernels: ./bin/kconfig-hardened-check -c /path/to/.config
Certain configuration options are required for a bootable system. Miss these and your kernel will panic early in boot.
Absolute Requirements:
CONFIG_EXT4_FS must be Y or in initramfs. Can't load module from a disk you can't mount!CONFIG_BLK_DEV_NVME), SATA (CONFIG_AHCI), or whatever your boot disk requires.CONFIG_PARTITION_ADVANCED and appropriate types (GPT, MBR).CONFIG_VT, CONFIG_VT_CONSOLE for seeing boot messages.CONFIG_BLOCK, CONFIG_BLK_DEV.CONFIG_EFI, CONFIG_EFI_STUB for UEFI boot.The initramfs Solution:
Distribution kernels solve the boot driver problem using initramfs (initial RAM filesystem). The bootloader loads a compressed cpio archive containing essential modules. The kernel:
To create initramfs:
# Debian/Ubuntu
$ mkinitramfs -o /boot/initrd.img-6.6.0 6.6.0
# Fedora/RHEL
$ dracut --force /boot/initramfs-6.6.0.img 6.6.0
# Arch
$ mkinitcpio -p linux
If building without initramfs, all boot-critical drivers must be built-in:
# Make storage and filesystem built-in
$ ./scripts/config --enable CONFIG_EXT4_FS # Not =m!
$ ./scripts/config --enable CONFIG_BLK_DEV_NVME
$ ./scripts/config --enable CONFIG_AHCI
Kernel panic: VFS: Unable to mount root fs — This error means either the root device driver isn't available or the filesystem driver isn't available. If using initramfs, regenerate it after kernel install. If not using initramfs, ensure drivers are built-in (=y), not modules (=m).
Managing kernel configs across multiple machines, kernel versions, and development cycles requires systematic approaches.
make savedefconfig creates a minimal file with only non-default values (~5-20KB), perfect for git../scripts/kconfig/merge_config.sh base.config fragment1.config fragment2.config../scripts/diffconfig oldconfig newconfig shows what changed between configs../scripts/config --enable/--disable/--module instead of manual edits.1234567891011121314151617181920212223242526272829303132333435363738394041424344
# Fragment-based configuration workflow# Keep base config and purpose-specific fragments # base.config - common settings$ cat configs/base.configCONFIG_LOCALVERSION="-mycompany"CONFIG_MODULES=yCONFIG_SMP=yCONFIG_PREEMPT=y # security.config - security hardening$ cat configs/security.configCONFIG_SECURITY=yCONFIG_SECURITY_SELINUX=yCONFIG_FORTIFY_SOURCE=yCONFIG_STACKPROTECTOR_STRONG=yCONFIG_RANDOMIZE_BASE=y # debug.config - debugging features$ cat configs/debug.configCONFIG_DEBUG_INFO=yCONFIG_KASAN=yCONFIG_LOCKDEP=yCONFIG_PROVE_LOCKING=y # Build production kernel$ make defconfig$ ./scripts/kconfig/merge_config.sh \ configs/base.config \ configs/security.config$ make -j$(nproc) # Build debug kernel$ make defconfig $ ./scripts/kconfig/merge_config.sh \ configs/base.config \ configs/debug.config$ make -j$(nproc) # Save final config as defconfig for version control$ make savedefconfig$ mv defconfig configs/production-full.defconfig$ git add configs/$ git commit -m "Add kernel configs for v6.6"You can add custom configs to arch/<arch>/configs/. Then make myconfig_defconfig will work just like vendor defconfigs. This is ideal for maintaining device-specific or workload-specific configurations alongside the kernel source.
Cross-Compilation Configuration:
When building for a different architecture (e.g., ARM on x86 host):
# Set architecture and cross-compiler
$ export ARCH=arm64
$ export CROSS_COMPILE=aarch64-linux-gnu-
# Now configuration uses ARM64 Kconfig
$ make defconfig # ARM64 defaults
$ make menuconfig # ARM64 options only
Out-of-Tree Configuration:
To keep source tree pristine, build in a separate directory:
$ mkdir ../linux-build
$ make O=../linux-build defconfig
$ make O=../linux-build menuconfig
$ make O=../linux-build -j$(nproc)
# Output goes to ../linux-build/, source stays clean
Configuration Reproducibility:
For reproducible builds, embed configuration in kernel:
CONFIG_IKCONFIG=y # Store config in kernel
CONFIG_IKCONFIG_PROC=y # Expose at /proc/config.gz
Then extract from running kernel:
$ zcat /proc/config.gz > running.config
| Target | Purpose | Use Case |
|---|---|---|
make tinyconfig | Absolute minimum | Starting point for extreme minimization |
make allnoconfig | Disable everything | Base for additive configuration |
make allyesconfig | Enable everything (y) | Testing, coverage |
make allmodconfig | Enable everything (m) | Distribution-style builds |
make randconfig | Random config | Build testing, fuzzing |
make listnewconfig | Show new options | Version upgrade auditing |
make helpnewconfig | Help for new options | Understanding new features |
make kvmconfig | Options for KVM guest | Virtual machine optimizations |
The kernel's continuous integration uses randconfig to test random configurations, catching build issues across the vast configuration space. If you're developing kernel code, periodically testing with different configs helps ensure your changes work broadly.
What's Next:
Configuration complete, we move to Kernel Compilation—the process of transforming configuration and source into bootable kernel images and modules.
You now understand kernel configuration deeply—from Kconfig internals to practical workflows for building custom kernels. This knowledge is essential for system optimization, embedded development, and kernel debugging.