Linux kernel developer inspecting modular hardware inside an open home lab server
Kernel Development
William  

Make Menuconfig: Configure Linux Kernel Features Safely

Start with make menuconfig from the root of the Linux kernel source tree. That is the practical workflow for the make menu config query: it opens the ncurses configuration menu, lets you choose kernel features and modules, and saves the result to .config. Use make config only when you deliberately want a sequential text prompt. Neither command compiles the kernel. If either fails, read the exact error, inspect the relevant Makefile and Kconfig entry, and use logs or strace instead of pasting a random fix from a forum.

Last updated: 2026-08-21

What does make menuconfig actually do?

Run make menuconfig from the root of the kernel source tree and you get the ncurses front end to Kconfig, the kernel's configuration system. It reads the Kconfig files scattered through the tree, layers your existing .config or a fresh defconfig on top, and lets you toggle features in a menu.

You aren't editing the kernel here. You are editing one configuration file that decides what gets compiled into the kernel, built as a module, or left out. The menu is safer than hand-editing that file because it enforces dependencies for you.

The Kconfig system presents symbols such as CONFIG_NET and CONFIG_DEBUG_KERNEL. Each symbol can control a driver, filesystem, security feature, debugging option, or core kernel behavior. Kconfig also hides options whose dependencies aren't satisfied.

On save, Kconfig rewrites .config and regenerates the files that later build steps need. The command changes the build plan. It doesn't produce a kernel image, install modules, or test whether the resulting kernel will boot.

The tool works well on a headless machine over SSH. It needs a usable terminal and the ncurses development files. You don't need a graphical desktop for Linux kernel configuration.

If you want the grammar behind the symbols, read the Kconfig language reference. That tells you what config, depends on, select, choice, bool, and tristate actually mean. Skip tutorials that paraphrase those rules and then hide the important part.

What do you need before running Linux make menuconfig?

Open desktop computer beside organized hardware components for kernel development preparation

You need a real kernel source tree, a working build environment, ncurses development headers, and a starting configuration. Configuration and compilation overlap, but they aren't the same job.

The source tree must contain the top-level Makefile, the root Kconfig, and directories such as arch, drivers, fs, and net. Running the command from a random directory gives you a predictable error because the top-level Makefile owns the target.

Check the directory before running anything:

cd /path/to/linux
test -f Makefile && test -f Kconfig
pwd

The output of pwd tells you which tree you are changing. The test commands print nothing when both files exist. If either test fails, stop there and find the source root.

The ncurses development package supplies the headers and libraries used by the terminal interface. The exact package name depends on your distribution. A missing header usually produces an error mentioning ncurses.h or a failed link against a menu library.

A complete kernel build needs more than menuconfig. It may also need a compiler, linker, assembler, make, a packaging toolchain, and architecture-specific tools. You can open the configuration interface before every full compilation dependency is ready, but you still need the complete toolchain to build the kernel afterward.

You also need a usable starting configuration. An existing .config is best when you are changing a kernel that already works. Otherwise, create a baseline with make defconfig for the target architecture.

Do not start by deleting .config. That throws away useful device, filesystem, and security choices before you know what you need.

How do you run make menuconfig in the Linux kernel source tree?

Use the source directory first, set the target architecture when necessary, then open the menu:

cd /path/to/linux
make defconfig
make menuconfig

make defconfig creates a maintainers' default configuration for the selected architecture. If you already copied a working configuration into .config, skip that command. Running it afterward would replace the configuration you intended to edit.

For a native build, the basic command is enough. For a cross-build, pass ARCH and CROSS_COMPILE:

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- menuconfig

ARCH selects the target architecture's Kconfig tree. CROSS_COMPILE selects the tool prefix used by later build steps. Set these consistently for configuration and compilation, or you can configure one target and build another. The kernel won't stop you from making that mistake. It assumes you meant it.

Inside the menu, use the arrow keys to move, Enter to open a submenu, and Escape to go back. Press Space to change a setting between disabled, built in, and module when the symbol supports those choices. Press / to search for a symbol.

The search screen is often faster than walking the menu tree. It shows the symbol's prompt, definition file, dependencies, selected symbols, and menu path. If a search result has no usable location, the symbol may be set by architecture logic or forced by another option. Scrolling harder won't make it editable.

When you leave the menu, it asks whether to save. Save the configuration, then confirm that .config changed:

grep '^CONFIG_' .config | less
git diff -- .config

The first command lets you inspect enabled settings. The second works when the source tree is tracked by Git and shows the exact configuration change. If there is no diff, you may have exited without saving or changed a value that was later normalized by Kconfig.

How does make config differ from make menuconfig?

Use make menuconfig for normal interactive work. Use make config only when you specifically want text prompts in sequence.

TargetInterfaceMain useMain drawback
make menuconfigncurses terminal menuInteractive configuration over a local terminal or SSHNeeds ncurses development files
make configSequential text promptsScripted or deliberately linear configurationGoing back is awkward
make nconfigncurses menu with additional interface featuresTerminal configuration with a different menu layoutStill needs ncurses development files
make xconfigQt graphical windowDesktop configuration with Qt availableNeeds Qt and a display
make gconfigGTK graphical windowDesktop configuration with GTK availableNeeds GTK and a display

All these front ends edit the same .config. They differ in how they present the Kconfig tree and what libraries they need.

make config walks through symbols line by line. It can be useful in a controlled script or when you need to answer every prompt explicitly. It is a poor choice for exploring a large kernel configuration because a missed dependency or wrong answer can send you through a long prompt sequence with little context.

The graphical targets aren't automatically better. They add desktop dependencies and are inconvenient on a server. For most SSH sessions, make menuconfig is the sane default.

How do you enable features and modules?

Read the symbol's help text and dependencies before changing it. Open its Kconfig entry, read the help text and the depends on line, and you will know exactly what it touches before you compile. That habit is what separates linux menuconfig from cargo-cult kernel building.

A symbol may be disabled, built into the kernel, or built as a loadable module. The available choices depend on the symbol type and its dependencies.

  • Disabled means the related code isn't built.
  • Built in means the code is part of the kernel image and is available during early boot.
  • Module means the code is built separately and can be loaded later.

A filesystem needed to mount the root device usually belongs in the kernel when the root filesystem must be available before modules can load. A driver for hardware used later in boot may work as a module, provided the initramfs contains it or the system can load it afterward.

The same distinction matters for storage, networking, input devices, virtualization, and security hooks. Enabling everything as built in wastes space and makes the result harder to understand. Disabling everything to create a small kernel breaks hardware in ways that only appear on the machine you didn't test.

Debugging features have their own cost. Options for debug information, lock checking, tracing, sanitizers, and verbose warnings can help find a fault, but they can change timing and increase memory use. Enable them for a reason, record the change, and remove them from a production configuration when the investigation ends.

If an option is missing, check its dependencies instead of editing .config until the symbol appears. A hidden option usually has a reason. Kconfig is doing less damage than the person trying to override it.

For a focused explanation of enabling symbols, see enable kernel configuration options. If the setting controls a driver, the follow-up work may involve Linux kernel modules.

Where does Linux kernel configuration come from?

The main configuration file is .config in the kernel source root. It is plain text and contains entries such as these:

CONFIG_NET=y
CONFIG_SOME_DRIVER=m
CONFIG_CUSTOM_NAME="example"

y builds a feature into the kernel. m builds it as a module when the option supports modules. A commented entry means the symbol is disabled.

You can inspect the file with normal text tools:

less .config
grep '^CONFIG_FS' .config
grep 'CONFIG_DEBUG' .config

That output tells you what Kconfig actually saved, not what you thought you selected in the menu.

An existing configuration can come from a running distribution kernel. Some systems expose it through /boot/config-$(uname -r). Others expose a compressed copy through /proc/config.gz. Copy it into the source root, then reconcile it with the source tree:

cp /boot/config-$(uname -r) .config
make olddefconfig
make menuconfig

The path may not exist on your system. Check it rather than assuming the distribution exposes its configuration.

make defconfig creates a baseline from architecture defaults. A distribution configuration gives you a closer starting point for the hardware and features you already use. Neither is a promise that the result will boot. The source version, architecture, boot loader, initramfs, firmware, and storage layout still matter.

From .config, the build generates files under include/generated/, including autoconf.h. Kernel source uses those generated definitions for conditionals such as #ifdef CONFIG_FOO. Don't edit generated headers. They are output, and the next configuration or build step will replace them.

The check I run after a save is direct:

grep 'CONFIG_DEBUG_INFO' .config

That confirms whether the symbol landed with the value I intended. If it is wrong, the save did not take or a dependency changed it. Reopen the menu before compiling the wrong kernel.

How do Kconfig files and Makefiles fit together?

Hands arranging modular computer components around a motherboard to represent kernel configuration

Kconfig decides which symbols exist and which values are allowed. Makefiles decide what source files the build uses after those values are set.

The top-level Makefile owns the menuconfig target. It invokes the Kconfig front end and points it at the root Kconfig file. That root file pulls in subsystem files with source lines, so options from drivers, filesystems, networking, and architecture code appear in one menu.

A Kconfig entry defines a symbol, type, prompt, help text, and dependencies. For example:

config MYTHING
 tristate "Support for my thing"
 depends on NET
 help
 Enables the my-thing driver.
 Say M here to build it as a module.

The depends on line controls whether the option can appear or be changed. A select line can force another symbol on, so read both sides when a value looks surprising.

The related Makefile connects the symbol to an object:

obj-$(CONFIG_MYTHING) += mything.o

When CONFIG_MYTHING=y, the object is built into the kernel according to that directory's rules. When it is m, the build produces a module. When it is disabled, the object is skipped.

A parent Kconfig file must also include the new entry:

source "drivers/mything/Kconfig"

Without that line, the symbol can exist in a file and still never appear in the menu. Without the Makefile entry, the menu can show a successful choice that produces no driver object. That is why Kconfig and Makefiles must be diagnosed together.

For a fuller walkthrough of this connection, see how Kconfig controls Linux kernel build options.

What happens to new symbols across kernel versions?

An old .config rarely matches a different kernel source tree perfectly. New symbols appear, old symbols disappear, and dependencies change.

make oldconfig asks about each new symbol and shows its default. make olddefconfig accepts the defaults without stopping for prompts.

olddefconfig is useful for routine rebuilds. It is also easy to misuse. A new hardening or security option can receive the maintainer's default without you seeing the decision.

When the source change is significant, run:

make oldconfig

Read the prompts instead of pressing Enter through them. That is where the new configuration choices become visible.

For a small update within the same kernel series, olddefconfig is usually less disruptive. For a larger move, oldconfig gives you a record of what changed. The correct choice depends on whether you need speed or deliberate review.

Which environment variables change Kconfig?

ARCH and CROSS_COMPILE cause many confusing results.

ARCH selects the target architecture and determines which arch/*/Kconfig files are included. If an expected option is absent, confirm that Kconfig is loading the architecture you intended.

CROSS_COMPILE sets the prefix for the cross-compiler tools. It matters most during compilation, but keeping it consistent in your workflow prevents configuration and build decisions from drifting apart.

You can pass both variables on each command:

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- menuconfig
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- olddefconfig
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu-

You can also export them in the shell:

export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
make menuconfig

Check the environment before blaming Kconfig:

printf 'ARCH=%s\n' "$ARCH"
printf 'CROSS_COMPILE=%s\n' "$CROSS_COMPILE"

An empty value may be correct for a native build. A wrong non-empty value is worse because it looks intentional.

Other variables can affect where output goes. O=/path/to/build keeps generated files outside the source tree:

make O=/path/to/kernel-build defconfig
make O=/path/to/kernel-build menuconfig

When you use an output directory, run later commands with the same O= value. Otherwise, you may inspect one .config while building from another. This is a common way to debug a configuration that was never used.

How do you troubleshoot a failed make menuconfig command?

Technician examining disconnected server components while troubleshooting a Linux build environment

Read the first real error and classify it. Don't start by reinstalling the whole toolchain or deleting the source tree.

The command says the target or Makefile is missing

Check the directory:

pwd
ls
test -f Makefile
test -f Kconfig

If the top-level Makefile isn't there, change to the kernel source root. If the files are present, inspect the target definition:

grep -n 'menuconfig' Makefile scripts/kconfig/Makefile

That tells you whether the source tree contains the expected Kconfig target and where the build is dispatching it.

The ncurses check fails

Look for the exact missing header or library in the output. A failure mentioning ncurses.h, menu.h, or a linker symbol points to the development package, not to .config.

You can inspect the Kconfig build rule:

grep -n 'ncurses\|menuconfig' scripts/kconfig/Makefile

Then confirm whether the compiler can find the header:

printf '#include <ncurses.h>\n' | cc -x c -E - >/dev/null

A successful command prints nothing and returns a success exit code. A failure tells you the compiler cannot use the header in its current environment.

Do not replace menuconfig with a graphical target just because the terminal front end failed. That adds Qt or GTK dependencies and changes the problem.

The configuration is stale or rejected

An old .config may contain symbols that no longer exist. Run make olddefconfig to reconcile it, or use make oldconfig when you need to review each new choice:

make olddefconfig
make menuconfig

If Kconfig reports a malformed line, inspect the surrounding entries:

grep -n 'CONFIG_' .config | less
sed -n '1,80p' .config

A hand-edited line can break parsing or create a value that dependencies immediately overwrite. Change the symbol through the menu or its Kconfig definition instead.

Permission errors appear

The source tree and output directory must be writable by the user running make. Check ownership and permissions:

namei -l /path/to/linux/.config
ls -ld /path/to/linux

namei -l walks each directory in the path. If one parent directory blocks access, changing .config alone won't fix it.

Don't run the whole configuration as root to hide a bad ownership problem. That leaves root-owned generated files behind and creates the next failure under your normal account. Fix the directory ownership or use a writable output directory.

The wrong architecture or option set appears

Print ARCH and CROSS_COMPILE, then inspect the architecture Kconfig files:

printf 'ARCH=%s\n' "$ARCH"
printf 'CROSS_COMPILE=%s\n' "$CROSS_COMPILE"
find arch -path '*/Kconfig' -type f | head

If the expected symbol is missing, search its definition:

grep -Rnw --include=Kconfig 'config MYTHING' .

That tells you which file defines it. Read the surrounding depends on, select, if, and source lines. The menu is showing the result of those rules, not making an arbitrary decision.

The command hangs or exits without a useful message

Capture the system calls:

strace -f -o menuconfig.strace make menuconfig
tail -50 menuconfig.strace

The trace shows which file, terminal device, library, or process caused the failure. Look for ENOENT when a file is missing, EACCES for permission problems, and failed openat calls around the last operation.

Also inspect the shell's exit code:

make menuconfig
printf 'exit code: %s\n' "$?"

A nonzero exit code confirms failure, but the command output and trace explain why. The exit code is the signal. It isn't the diagnosis.

How do you verify and use the saved configuration?

Configured generic server hardware assembled and ready for kernel testing in a home lab

First confirm that .config exists in the tree you actually configured:

test -s .config
grep '^CONFIG_' .config | head

If you used O=, check the output directory instead:

test -s /path/to/kernel-build/.config

Next, review the changes:

git diff -- .config

If the source isn't tracked, compare a backup:

cp .config .config.before
make menuconfig
diff -u .config.before .config

The diff tells you whether the intended symbol changed and whether Kconfig adjusted related dependencies. It also gives you a reproducible record for the next build.

Run the configuration checks before compiling:

make olddefconfig
make listnewconfig

olddefconfig resolves missing symbols using defaults. listnewconfig shows symbols that still need attention when the source tree and configuration don't line up. Read that output instead of assuming the menu saved a complete answer.

Then start the kernel build with the same architecture, compiler, and output settings:

make

A successful make menuconfig only means Kconfig accepted and saved the selections. It does not prove that the compiler can build them, that the initramfs contains required modules, or that the resulting kernel will boot.

After compilation, inspect the build output and logs when something fails. If a selected feature produces no expected object, return to its Kconfig entry and the subsystem Makefile. If the kernel builds but hardware disappears, check whether the driver was set to m, whether the module was installed, and whether the boot environment can load it.

Configuration is a plan. Compilation and boot are separate tests.

Where do people get Linux menuconfig wrong?

The usual mistakes are procedural, not mysterious.

  • Running the command outside the kernel source tree.
  • Treating make menuconfig as a compiler command.
  • Starting with an empty configuration when a working distro configuration exists.
  • Selecting every driver and debug feature because storage is cheap.
  • Setting a driver to a module when the root filesystem needs it before modules can load.
  • Editing generated headers instead of changing the Kconfig symbol.
  • Copying an old .config into a new source tree without running oldconfig or olddefconfig.
  • Using the wrong ARCH and then blaming the missing menu entry.
  • Running sudo make menuconfig and leaving root-owned files behind.
  • Trusting a forum command without reading the Makefile or the Kconfig dependency.

The safe pattern is plain: start from a known configuration, change a narrow set of symbols, inspect .config, reconcile version changes, then build with the same target settings. Keep the diff. If the result is wrong, you can identify what changed instead of rebuilding from memory.

FAQ

Can I run make menuconfig without compiling the kernel?

Yes. The command only runs Kconfig, updates .config, and generates configuration output. You still need a full compiler and build environment for the later kernel build.

Why does make menuconfig show fewer options than a guide?

The selected architecture, unmet dependencies, and parent menu conditions can hide symbols. Search for the symbol with /, then read the dependency information shown by Kconfig.

Can I use make menuconfig on a distribution kernel?

You need the matching or compatible kernel source tree, not only the binary kernel installed on the machine. A copied distribution configuration can provide a useful baseline, but Kconfig still has to reconcile it with the source version.

Where should I keep a custom kernel configuration?

Keep the working .config with the build record, and save a separate copy when the configuration matters. If you use an external output directory, preserve that directory's .config and the exact ARCH, CROSS_COMPILE, and O= settings used for the build.

Why did Kconfig change a setting I selected?

A dependency, select, default, or architecture rule changed the result during normalization. Find the symbol's Kconfig definition and read the nearby conditions. The saved .config is the final answer, not the visual state you remember from the menu.