Developer workspace with laptop showing kernel development code and technical references
Kernel Development
William  

How Menuconfig Configures the Linux Kernel

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. On save it rewrites .config and regenerates the header files the build depends on. If you are not sure what a symbol does, do not guess from a forum thread. 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.

Last updated: 2026-07-21

What does linux menuconfig actually do?

It is a text menu over Kconfig, nothing more and nothing less. The kernel source is full of Kconfig files, one per subsystem, and each defines symbols like CONFIG_NET or CONFIG_DEBUG_KERNEL. make menuconfig parses all of them, builds a tree, and paints it with ncurses so you can walk it in a terminal.

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

The tool shines on a headless box over SSH with no X server. It needs a terminal and the ncurses library, and that is it. If you want the real grammar behind the symbols, read the Kconfig language reference. That is the primary source. Skip the tutorials that paraphrase it.

How Kconfig files and the Makefile fit together

The top-level Makefile owns the whole thing. When you type make menuconfig, that Makefile invokes the Kconfig front end and points it at the root Kconfig file. That root file pulls in the rest with source lines, so every subsystem's options get stitched into one tree.

Each entry defines a symbol, its type (bool, tristate, string, int), a prompt, help text, and its dependencies. The depends on line decides whether you even see the option. The select line lets one symbol force another on.

Here is what happens on save: Kconfig writes your choices back to .config. Later, when you run the build, the Makefile reads .config and each subsystem Makefile uses lines like obj-$(CONFIG_NET) += net/ to decide what to compile. So the menu, the config file, and the build are three stages of one pipeline.

What happens when you run make menuconfig?

Kconfig loads your current .config first, then opens the menu with those values pre-selected. Arrow keys and Enter drill in, Esc backs out. Space toggles a symbol between off, built-in (*), and module (M) where the symbol allows a module.

Press / to search. This is the flag that saves the most time. Type a symbol name and it prints the prompt, the Kconfig file that defines it, the depends on line, any select clauses, and the menu path to reach it.

When you exit, it asks whether to save. Say yes and it writes .config in the source root. If the search shows no location for a symbol, that symbol is not user-selectable. It is being set by an architecture Kconfig or forced on by another option's select, and no amount of scrolling will let you toggle it directly.

menuconfig against make config, xconfig, and the other front ends

They all edit the same .config. The difference is the interface and what you need installed. Reach for the one that matches your terminal, not the one a blog told you to use.

Front endCommandInterfaceNeeds
configmake configone question at a time, line by linenothing extra, but brutal for a full config
menuconfigmake menuconfigncurses menu in a terminalncurses dev headers
nconfigmake nconfignewer ncurses menu with F-key helpncurses dev headers
xconfigmake xconfigQt graphical windowQt libraries and a display
gconfigmake gconfigGTK graphical windowGTK libraries and a display

make config walks every single symbol and never lets you go back. Do not use it for a real config unless you enjoy pain. The graphical ones are fine on a desktop, but they drag in Qt or GTK and need a display you rarely have on a server. For most work over SSH, menuconfig is the sane default. It hangs on the GNU ncurses library.

What .config and the generated headers actually hold

.config is a plain text file of CONFIG_ lines. Each is either CONFIG_FOO=y, CONFIG_FOO=m, a value like CONFIG_FOO="string", or a commented-out # CONFIG_FOO is not set. You can read it with less .config and grep it. Do that after every save instead of trusting the menu closed cleanly.

From .config, the build generates headers under include/generated/, including autoconf.h. These translate your config into C preprocessor defines so the source can do #ifdef CONFIG_FOO. You do not edit these. They are output, regenerated on every build, and hand-editing them just gets overwritten.

The check I run after a save is simple. grep CONFIG_DEBUG_INFO .config confirms the symbol I meant to change actually landed. If the value is wrong, the save did not take, or a dependency silently forced it back. That tells you to reopen the menu before you waste an hour compiling the wrong kernel. If you are turning on debug symbols, the details in building a kernel with debug symbols are worth a read.

Starting from make defconfig or a distro config

Never start a config from nothing. make defconfig writes a sane .config for your architecture using the maintainers' defaults, and you layer your changes on top. It is the honest baseline.

If you want to build something close to what you already run, copy your distro's config instead. On most systems it lives at /boot/config-$(uname -r) or in /proc/config.gz. Copy it to the source root as .config, then run make olddefconfig to fill in any symbols your distro's kernel version did not have.

zcat /proc/config.gz > .config # if your kernel exposes it
make olddefconfig
make menuconfig

The order matters. Get a known-good .config in place first, then open menuconfig to change the two or three symbols you care about. Editing one setting at a time keeps the build reproducible and keeps you from wondering later why the kernel behaves differently than the distro's.

Adding a new kernel config option, step by step

Say you wrote a driver in drivers/mything/ and want it in the menu. You wire it in two places: a Kconfig entry so the symbol exists, and a Makefile line so the build honors it.

  1. Add an entry to drivers/mything/Kconfig:
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.
  1. Make sure a parent Kconfig sources yours, for example a line source "drivers/mything/Kconfig" in drivers/Kconfig.

  2. Wire the build in drivers/mything/Makefile:

obj-$(CONFIG_MYTHING) += mything.o

Now make menuconfig shows "Support for my thing" under the parent menu, gated by NET. The tristate type is what lets a user pick module (M); use bool if it can only be built in. If the option never appears, your depends on is unmet or no parent Kconfig sources your file. There is a fuller walkthrough in using Kconfig for a new kernel module if you are doing this for the first time.

What happens to new config symbols across kernel versions

New kernel, new symbols. When you carry an old .config into a newer tree, dozens of options exist that your file has no value for. make oldconfig prompts you for each one, showing the default. make olddefconfig skips the prompts and accepts every default silently.

olddefconfig is fast and fine for a quick rebuild. But it means a new security or hardening symbol can land in your kernel as whatever the maintainers defaulted it to, without you ever seeing it. That is the gotcha nobody mentions.

So when the version jump is large, I run make oldconfig and read the prompts. It is tedious, but it is the one moment you learn what changed. For a routine patch bump on the same series, olddefconfig is fine. The point is to make the choice on purpose, not to mash Enter through a hundred defaults and hope. If you are moving between major releases, the step-by-step kernel update guide covers the surrounding work.

The environment variables that change how Kconfig runs

Kconfig reads a handful of variables before it does anything. Get these wrong and you will configure the wrong architecture without noticing.

  • ARCH sets the target architecture, for example ARCH=arm64. This decides which arch/*/Kconfig gets pulled in, and it is the single most common cause of "my option disappeared."
  • CROSS_COMPILE names your cross toolchain prefix, like CROSS_COMPILE=aarch64-linux-gnu-, for building on one machine to run on another.
  • KCONFIG_CONFIG points Kconfig at a config file other than .config, handy when you juggle several configs in one tree.

Export them before you run the tool, in the same shell:

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

If you set ARCH for menuconfig but forget it for the build, the two disagree and the build fails in confusing ways. Set them once at the top of your shell session and keep them consistent through both steps.

Where people get linux menuconfig wrong

The most common failure is a stale .config. You edit an old config in a new tree, skip oldconfig, and half your symbols carry defaults you never chose. Always reconcile the config against the tree first, then open the menu.

The second is the missing library. make menuconfig dies with an error about curses.h when the ncurses dev headers are not installed. Read the actual error. It names the header. Install libncurses-dev (Debian and Ubuntu) or ncurses-devel (Fedora and RHEL) and it launches. Do not copy a random "reinstall your kernel headers" fix; the build log already told you what is missing.

Third is the wrong ARCH. An option is documented as existing, but your search finds nothing, because you are configuring x86 while looking for an arm64 symbol. Confirm echo $ARCH before you blame the menu.

When something still will not show, stop guessing and read the source. grep -R CONFIG_YOURSYMBOL across the tree finds where it is defined and who selects it. That one command answers "why can't I toggle this" faster than any forum thread, because it shows you the real depends on and select chain. For the follow-on work of flipping options safely, enabling kernel CONFIG options goes deeper.

FAQ

Do I need to rebuild the whole kernel after changing one option in menuconfig?

You run make in the source directory, but it only recompiles what your change touched, not the entire tree from scratch. Flip a built-in symbol and expect broad recompilation. Flip a single module and the rebuild is small. There is no runtime "apply" step; the change takes effect once you build and boot the new kernel or load the new module.

Can I share my .config with someone and expect the same kernel?

Only if you are both on the exact same kernel version and architecture. .config symbols map to a specific source tree, so a config from one version dropped into another leaves new symbols unset until you run oldconfig. Ship the config plus the kernel version and the ARCH you used, and tell them to run make olddefconfig first.

How do I search for an option when I do not know its exact name?

Press / inside the menu and type a partial string; the search matches symbol names and prompts. If that comes up empty, drop to a shell and grep -Ri "your feature" $(find . -name Kconfig) to find the defining file. Reading the Kconfig entry directly also shows the depends on line, which tells you why the option might be hidden.

Why does an option show up but refuse to let me turn it off?

Because another symbol selects it. Search for it with / and read the Selected by line; whatever is listed there is force-enabling it. You cannot toggle the child directly. Turn off the parent symbol that selects it, and the locked option becomes editable again.

Is menuconfig the same on every distribution?

The tool is part of the kernel source, so it behaves identically wherever you build. What differs is the package name for the ncurses headers it needs and the location of your distro's shipped config. Those are distribution details, not menuconfig differences.

Related: Auto-Replace OpenWrt Kernel Config Without Losing Changes