
How Kconfig Controls Linux Kernel Build Options
Kconfig is the language and toolset that builds the Linux kernel's configuration menus and writes the .config file that drives the whole kernel build. Nothing about it is hidden. Every symbol's behavior is spelled out in the Kconfig source files. So if an option won't set, refuses to appear, or breaks the build after you flip it, the answer lives in those files and in the generated .config. You find it by reading the source and diffing the config, not by pasting a stranger's fix from a forum.
Last updated: 2026-07-22
What Kconfig actually is
Kconfig solves one problem: the kernel has tens of thousands of build options, and most of them depend on each other. You cannot turn on a filesystem that needs a block layer that is switched off. Kconfig is the rule system that keeps those choices consistent.
It does three jobs. It defines the options and their relationships in a small declarative language. It renders those options into a menu you can navigate. And it writes out a .config file that Kbuild reads to decide what gets compiled.
The .config is the only output the build cares about. The menus are a front end. When people say "kconfig" they usually mean the whole stack: the language, the parser under scripts/kconfig/, and the config file it produces. Keep those three separate in your head and most confusion goes away.
Where Kconfig files live in the kernel tree
The files are all named Kconfig, and they sit in nearly every subdirectory of the kernel source. That naming is the convention, not a suggestion. drivers/net/Kconfig owns the network driver options, fs/Kconfig owns filesystems, and so on down the tree.
They chain together with source statements. The top-level Kconfig at the root pulls in arch/$ARCH/Kconfig, which pulls in drivers/Kconfig, which pulls in each subsystem. So the menu tree you see in menuconfig is just this file graph flattened out.
Kbuild is the other half. Once Kconfig writes .config, the build reads symbols like CONFIG_EXT4_FS and matches them against obj-$(CONFIG_EXT4_FS) lines in the Makefiles. The config decides y, m, or n; the Makefile turns that into a built-in object, a loadable module, or nothing. For the deeper mechanics of the front end, see how to configure the kernel with make menuconfig.
How is a Kconfig file structured?

A Kconfig file is a flat list of entries, and the two you meet first are config and menu. A config entry declares one symbol. A menu/endmenu pair groups entries under a heading. That is most of it.
Here is a minimal entry:
config MY_DRIVER
tristate "Support for my widget"
depends on I2C
default n
help
Enable this to build the widget driver. Say M to
build it as a loadable module.
The first line names the symbol. The kernel prepends CONFIG_ to it everywhere else, so this becomes CONFIG_MY_DRIVER in .config and in code. The quoted string on the type line is the prompt, the text you see in the menu. No prompt means no menu entry, and that catches people constantly.
Other structural keywords do exactly what they read like. source "path/Kconfig" includes another file. choice/endchoice forces one pick out of several. if/endif wraps a block in a shared dependency so you don't repeat depends on on every line. comment prints a line of text in the menu. Indentation is cosmetic; the keywords carry the meaning.
The core symbol types and attributes
Every config symbol has a type, and the type decides what values it can hold. There are five, and you will use two of them almost all the time.
| Type | Values it holds | Typical use |
|---|---|---|
bool | y or n | A feature that is built-in or absent |
tristate | y, m, or n | Anything that can be a loadable module |
string | text | A path or name |
int | decimal number | A size or count |
hex | hex number | An address or mask |
Use tristate when the code can be a module. Use bool when it genuinely cannot, like core scheduler or early-boot code that has to be compiled in. Picking bool for something that should load on demand just annoys people who wanted modprobe.
The attributes hang off the type. default sets the starting value; prefer default n unless the option is broadly safe and useful. prompt is the menu text, usually written inline with the type. select S forces symbol S on when this one is on. depends on gates the symbol, which earns its own section next.
How does 'depends on' control visibility and values?
depends on EXPR does two things at once, and mixing them up is where hours disappear. First, if EXPR is false, the symbol has no menu entry at all. Second, even when set from a .config or a select, the symbol can never rise above what EXPR allows.
So a tristate with depends on I2C cannot be y if I2C=m. The dependency caps the value. That is why your option is greyed out or simply gone: a parent it needs is off, or set lower than you want.
Do not guess which parent. Trace it. Open menuconfig, press /, and search your symbol by name. The search screen prints the symbol's type, its prompt, and every Depends on: line with each term's current value. Read that list top to bottom and find the term showing n. That term is your real problem. The symbol you were staring at is fine.
If you would rather read raw source, grep the tree: git grep -n "config MY_DRIVER" -- '*/Kconfig' finds the definition, and the depends on lines under it are the whole truth. The menu is a rendering of those lines. When the two seem to disagree, the file wins and you misread the render. For turning found dependencies into set options, this walkthrough on enabling kernel config options covers the follow-through.
What 'imply' does that 'select' does not
imply is a soft, reversible nudge; select is a hard force. That single difference decides which one wrecks your config. select FOO turns FOO on and the user cannot turn it back off from the menu. imply FOO sets FOO to y as a default but leaves the user free to drop it to m or n.
Here is what actually happens with imply. When symbol A has imply B, enabling A makes B's default become y, so B comes on unless the user or another rule says otherwise. B's own depends on still applies. So imply respects both the dependencies and the user, while select runs over both.
Reach for imply when a feature works better with B but does not require it. Reach for select only when the code will not compile without B, and even then check B's dependencies first. A select on a symbol with unmet depends on is the classic recursive-dependency error, and the build will tell you so in a wall of text pointing at both symbols. Read that error; it names the exact loop.
To trace an imply chain, do the same search you use for dependencies and read the Selected by and Implied by lines the search screen prints. Those two lines tell you who is reaching in and setting your symbol behind your back.
How menus and hierarchy organize the tree
The menu you scroll through is built entirely from menu, menuconfig, and if blocks plus the source chain. There is no separate layout file. Move a source line and the tree reshuffles.
A menuconfig symbol is the useful trick here. It is a normal option that also acts as a collapsible heading: the entries under it appear only when it is on, and hide when it is off. That keeps a subsystem's twenty sub-options out of your face until you enable the subsystem itself.
Visibility flows from dependencies, always. An entry with an unmet depends on does not show up, and neither does its submenu. So an "empty" or missing menu section is not a bug in Kconfig. It means every option inside depends on something you have switched off. Turn on the parent and the branch reappears.
Setting Kconfig values by hand
Four front ends write the same .config, and they differ only in how they draw the menu:
make menuconfig– the ncurses text menu. This is the one to learn. It runs over SSH, needs only a terminal, and the/search is the fastest way to find and diagnose a symbol.make nconfig– a newer text menu with a friendlier layout and inline help. Same job, different paint.make xconfig– a Qt graphical menu. Fine on a desktop with X, useless on a headless box.make config– asks every question in sequence with no menu. Skip it. It has thousands of prompts and no way back.
Whatever you pick, the output is one file, .config, at the root of the tree. Point the tools at a different path with the KCONFIG_CONFIG environment variable when you want to keep several side by side. After editing, make olddefconfig fills in any new symbols with their defaults so your config stays valid across kernel versions.
Reading a symbol's real help text
Trust the help block in the Kconfig file over any blog summary, this one included. It is written by the person who wrote the code, it ships with the exact kernel version you are building, and it lists the constraints that matter for that version.
Two ways to read it. In menuconfig, highlight the option and press ? or h; it prints the full help plus the symbol name, type, and dependency lines. From a shell, git grep -A 20 "config MY_DRIVER" -- '*/Kconfig' dumps the definition and its help in one go.
Why bother when a summary is faster? Because help text changes between releases. An option that was experimental two versions ago may be stable now, or renamed, or folded into another symbol. The in-tree text is correct for your source; a web page is correct for whenever someone wrote it. The authoritative grammar for all of this is the kernel's own Kconfig language reference, which defines every keyword precisely.
How do you debug a Kconfig dependency failure yourself?
Read the error, then diff the config. When a build breaks right after a config change, cp .config .config.broken, revert the change, regenerate, and run diff .config.old .config.broken. That diff is the exact set of symbols that flipped. Nine times out of ten your regression is one line in it.
For a symbol that won't set, the search screen in menuconfig is the whole diagnosis. It prints the dependency chain with live values. Find the term reading n, and that is the option to enable first. You do not fix the symbol you wanted; you fix its unmet parent.
What if the recursive-dependency error names two symbols?
The message names both symbols in the loop. Open each one's definition with git grep and follow the select and depends on lines between them until you see the cycle. The fix is almost always swapping a select for a depends on, because select ignores dependencies and creates loops that depends on cannot.
When even that is murky, the parser lives in scripts/kconfig/. It is plain C and it is readable. conf.c and symbol.c show exactly how a symbol's value is computed from its type, defaults, and dependencies. Reading that code once settles arguments you would otherwise have with a forum thread forever.
Kconfig on embedded targets
On a constrained board you start from a defconfig, not from scratch. A defconfig is a stripped .config holding only the non-default symbols, checked into arch/$ARCH/configs/. Run make my_board_defconfig to expand it, then make savedefconfig to write your changes back out in the same minimal form.
The discipline here is turning things off. Every y is bytes in the image, and on a device with tight flash you feel it. So embedded configs lean on depends on to keep unused subsystems out, and avoid select chains that quietly drag in code you did not want. A select on a big subsystem can balloon a minimal image without a single line in your defconfig admitting it.
Two habits save pain. Diff your defconfig against the board's default so a reviewer sees exactly what you changed and why. And build with the option both as m and as n before you ship, because a driver that only ever gets tested built-in tends to break the day someone tries to load it as a module.
FAQ
What is the difference between Kconfig and Kbuild?
Kconfig decides what to build; Kbuild does the building. Kconfig parses the Kconfig files, runs the menus, and writes .config. Kbuild then reads .config and walks the Makefiles to compile the selected objects. They are separate systems that meet at that one file.
Can I edit .config by hand instead of using a menu?
You can, but run make olddefconfig afterward. Setting CONFIG_FOO=y directly skips every dependency check, so you might enable something its parents forbid. olddefconfig reprocesses the file, drops values the dependencies won't allow, and fills in anything you left out. Editing by hand and then regenerating is fine; editing by hand and building blind is how you get silent misconfiguration.
Why does my CONFIG option not appear in menuconfig at all?
Almost always one of two things. Either the symbol has no prompt string, so it exists but is invisible by design, or its depends on is unmet and the whole entry is hidden. Search the symbol name with / in menuconfig; the search shows hidden symbols and prints why they are hidden. If the symbol isn't even found, its Kconfig file is never reached by a source line.
Is the KConfig used by KDE the same thing?
No. KDE's KConfig is an unrelated application settings framework that happens to share the name. The kernel's Kconfig is the build configuration language described here. If a search result talks about .kcfg files or Qt, you are on the KDE one and it will not help you configure a kernel.
How do I know which built option a running kernel actually has?
Check the config the running kernel was built with. Your source tree may have drifted. Many distros ship it at /boot/config-$(uname -r), and most kernels expose it live at /proc/config.gz if CONFIG_IKCONFIG_PROC is on. zcat /proc/config.gz | grep CONFIG_FOO tells you the truth about the kernel you are on right now. For working with the modules that config produces, this beginner's guide to Linux kernel modules is a good next stop.
