Three Approaches to Kernel Configuration
Embedded system, distro copy, or lean custom build? Here is how to pick the right starting config for every situation.
What you will learn in this part
- Approach 1: Using a board-specific defconfig for embedded Linux systems
- Approach 2: Starting from your distribution’s existing kernel config
- Approach 3: The localmodconfig approach — a lean, minimal config
- When to use which approach and the trade-offs of each
- How to verify your final config is consistent and correct
- Practical step-by-step commands for each approach
Why Your Starting Point Matters So Much
Picking the wrong starting config is one of the most common mistakes when building the Linux kernel for the first time. Here is why it matters so much in practice:
A typical Ubuntu or Debian kernel config enables thousands of drivers and features — including drivers for hardware that does not exist on your target machine. The kernel takes much longer to compile, the image is larger, boot time increases, and on embedded systems you may simply run out of flash storage.
If you start from a too-minimal config (like make defconfig) without understanding your hardware, you may miss the storage controller driver — and the kernel will panic at boot because it cannot find the root filesystem. Or you miss the network driver and the system is unreachable after deployment.
The three approaches below solve different versions of this problem depending on your target and goals. Let us go through each one carefully.
Board-Specific defconfig for Embedded Systems
If you are building Linux for a specific embedded board — a Raspberry Pi, a Rockchip SoC, a Qualcomm mobile platform, an NXP i.MX board — the Linux kernel source tree itself gives you a head start. The kernel developers and SoC vendors contribute ready-made, tested configuration files for a huge number of hardware platforms.
These files live in the kernel source under arch/<architecture>/configs/ and are named in the format <board-name>_defconfig.
arch/
├── arm/
│ └── configs/
│ ├── bcm2835_defconfig ← Raspberry Pi Zero / Pi 1 (BCM2835 SoC)
│ ├── imx_v6_v7_defconfig ← NXP i.MX6/i.MX7 boards
│ ├── omap2plus_defconfig ← TI OMAP / BeagleBone
│ ├── multi_v7_defconfig ← Generic ARM v7 multi-platform
│ └── ... ← 100+ board configs
├── arm64/
│ └── configs/
│ ├── defconfig ← Generic ARM64 (covers RPi 3/4/5)
│ ├── bcm2835_defconfig
│ └── ...
├── riscv/
│ └── configs/
│ ├── defconfig
│ └── rv32_defconfig
└── x86/
└── configs/
├── i386_defconfig
└── x86_64_defconfig
Step-by-Step: Using a Board defconfig
# Step 1: Find available defconfigs for your architecture
ls arch/arm64/configs/ # For 64-bit ARM (RPi 3, 4, 5)
ls arch/arm/configs/ # For 32-bit ARM boards
# Step 2: Load the board's defconfig as your starting .config
# This creates a .config from that board's defconfig file
make ARCH=arm64 defconfig # Generic ARM64
make ARCH=arm64 bcm2711_defconfig # Raspberry Pi 4 (BCM2711)
make ARCH=arm omap2plus_defconfig # BeagleBone Black
# Step 3: Optionally fine-tune with menuconfig
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- menuconfig
# Step 4: Build
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc) Image modules dtbs
# Step 5: Install modules (on the target or in a sysroot)
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- INSTALL_MOD_PATH=/path/to/rootfs modules_install
✅ Why this approach works well
- The defconfig was created and tested by someone who knows exactly what that board needs
- It includes only what is relevant for that hardware — no x86 PC Card drivers or irrelevant file systems
- Critical things like the correct SoC clock driver, MMC storage driver, and UART console are already enabled
- You can still add or remove things on top of this via menuconfig
Start from Your Distribution’s Config
If you are building a custom kernel for the same x86 machine you are sitting at — maybe you want a newer version, or need to add/remove a specific driver, or want to experiment with real-time patches — the safest starting point is your distribution’s own kernel config.
Your distro kernel has already been built and tested on your hardware. It knows which storage controller driver you need, which GPU driver, which filesystem. Starting from it means you inherit all of that knowledge.
Where to Find Your Distro’s Config
# Method 1: The /boot directory (most common on Ubuntu, Debian, Fedora)
ls /boot/config-$(uname -r)
# Example output: /boot/config-6.8.0-49-generic
# Method 2: /proc/config.gz — if running kernel has CONFIG_IKCONFIG_PROC=y
# (Common on Arch Linux, some Fedora builds)
zcat /proc/config.gz | head -10
# Method 3: /lib/modules directory
ls /lib/modules/$(uname -r)/.config 2>/dev/null
Step-by-Step: Using Distro Config
# Step 1: Download the kernel source (always from kernel.org)
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.12.tar.xz
tar xf linux-6.12.tar.xz
cd linux-6.12
# Step 2: Copy your running kernel's config as the starting point
cp /boot/config-$(uname -r) .config
# Step 3: Update the config for the new kernel version
# This handles new options added since your distro config was created
# It sets all new options to their defaults — no manual prompting
make olddefconfig
# Step 4: Open menuconfig to review or customize
# (optional — skip this if you just want the same config as your distro)
make menuconfig
# Step 5: Build the kernel
make -j$(nproc)
# Step 6: Install modules and then the kernel
sudo make modules_install
sudo make install # Copies kernel and updates bootloader on most distros
Ubuntu and Debian kernel configs often have CONFIG_SYSTEM_TRUSTED_KEYS set to a path pointing to Canonical’s/Debian’s kernel signing keys — which do not exist on your machine. You will get a build error. Fix it:
scripts/config --set-str CONFIG_SYSTEM_TRUSTED_KEYS "" scripts/config --set-str CONFIG_SYSTEM_REVOCATION_KEYS ""
✅ Advantages
- Guaranteed to support your hardware
- All critical drivers are already included
- Minimal risk of an unbootable kernel
- Great for learning — just change one thing at a time
⚠️ Disadvantages
- Very large config — 10,000+ options set
- Compile time is very long (1–3 hours on average hardware)
- Contains drivers for hardware you will never use
- Distro-specific patches may not apply cleanly to vanilla kernel
📊 How big is a typical Ubuntu kernel config?
# Count total config options grep -c "^CONFIG_" /boot/config-$(uname -r) # Typical output: ~10,000 to 12,000 options # Count enabled options (y or m) grep -c "=y\|=m" /boot/config-$(uname -r) # Typical output: ~6,000 to 8,000 options enabled # Count loadable modules enabled grep -c "=m$" /boot/config-$(uname -r) # Typical output: ~4,000+ modules
This is why a full distro config kernel takes so long to compile — there are thousands of modules being built.
The localmodconfig Approach
The localmodconfig approach is clever. Instead of asking you which drivers you want, it looks at your currently running system and asks: “Which kernel modules are loaded right now?” Then it creates a minimal kernel config that includes exactly those modules and turns off everything else.
The result is a very lean kernel that still fully supports your specific hardware — because if a module is currently loaded, it obviously means your hardware needs it. No more, no less.
Runs lsmod to get the list of all currently loaded kernel modules
For each loaded module, it looks up the corresponding CONFIG symbol in the kernel source
Generates a minimal .config with those modules enabled and everything else turned off
You can then add any additional options you need via menuconfig on top of this lean base
Step-by-Step: Using localmodconfig
# Step 1: Make sure all your hardware is active before running this
# Plug in USB devices, connect WiFi, mount drives, etc.
# localmodconfig only knows about CURRENTLY LOADED modules
# See what is currently loaded
lsmod | head -20
lsmod | wc -l # Typical: 80-150 modules on a desktop
# Step 2: Go into the kernel source tree
cd linux-6.12
# Step 3: Run localmodconfig
# If you have a previous .config to start from:
cp /boot/config-$(uname -r) .config
make localmodconfig
# Or start completely fresh (uses a generic defconfig as base):
make localmodconfig # Will ask about options it is unsure about
# Step 4: Review or customize
make menuconfig # Optional — add anything localmodconfig missed
# Step 5: Build
make -j$(nproc)
# Compare module count before and after:
# Before (distro config): ~4000 modules
# After (localmodconfig): ~100-200 modules — much faster build!
localmodconfig only captures modules that are currently loaded at the time you run it. If you have a USB WiFi dongle but it is not plugged in right now, its driver will not be included. If you have a Bluetooth headset but it is off, its driver may be missing. If you want to boot from a different filesystem later, it might not be compiled in.
Solution: Before running make localmodconfig, plug in all your hardware, connect WiFi, mount all drives, and use everything you plan to use. Then run the command. Alternatively, use make menuconfig after localmodconfig to manually add anything you know you will need.
| Starting Config | Modules Built | Compile Time |
|---|---|---|
| Distro config (Ubuntu) | 4,000+ | 60–120 min |
| Distro config + localmodconfig | 100–200 | 8–15 min |
| Board defconfig (ARM embedded) | 50–150 | 5–12 min |
Times vary significantly based on hardware, SSD vs HDD, and exact config
Choosing the Right Approach — Decision Guide
| Your Situation | Best Approach | Why |
|---|---|---|
| Building for Raspberry Pi / BeagleBone / embedded SoC | Approach 1 | Board defconfig already knows what that hardware needs |
| Custom kernel for your own laptop / desktop — reliability matters | Approach 2 | Distro config guarantees your hardware works — just add your customization |
| Fast iteration — want to recompile often while developing a driver | Approach 3 | localmodconfig gives a lean build — rebuild in minutes not hours |
| Learning — just want to experiment and see what options exist | Approach 3 | Quick builds mean faster feedback loop — build, test, change, repeat |
| Production system where stability is non-negotiable | Approach 2 | Start from a proven config, change minimum, test exhaustively |
Verifying Your Configuration is Consistent
Regardless of which approach you choose, after finalizing your .config you should always run these verification steps:
# 1. Resolve any dependency inconsistencies — ALWAYS run this after any config change
make olddefconfig
# This updates .config to satisfy all Kconfig dependencies
# Sets any missing options to their default values
# 2. Run a quick sanity check
make prepare
# This runs some pre-build checks without actually compiling
# 3. Check for new options you might have missed
make listnewconfig
# Lists all CONFIG options present in Kconfig but missing from your .config
# 4. Look at what you have enabled (useful after localmodconfig)
grep "=y" .config | wc -l # Count built-in features
grep "=m" .config | wc -l # Count modules
grep "not set" .config | wc -l # Count disabled features
# 5. Verify the config file version matches the kernel source
head -3 .config
# Should show: # Linux/x86_64 6.12.0 Kernel Configuration
Complete Series Recap
- CONFIG_FOO symbols
- y / m / n values
- Kconfig files
- Recursive Makefiles
- The .config file
- DEFCONFIG_LIST priority
- menuconfig / nconfig
- oldconfig / olddefconfig
- scripts/config utility
- Production vs learning
- Board defconfig (embedded)
- Distro config (safe start)
- localmodconfig (lean)
- Decision guide
- Config verification

2 Comments