Minimizing a Buildroot defconfig — What to Drop, Deps, savedefconfig
Shrinking a Buildroot image is not only “uncheck packages in menuconfig.” You also need a reproducible minimal defconfig. The useful loop is: decide what to turn off · understand dependency breakage · store and reload with savedefconfig.
This post stays on three axes only: what to turn off · what happens when deps break · how to reproduce. No invented board footprints, prices, field diaries, or affiliates.
Grounded in the Buildroot user manual — especially Storing the Buildroot configuration (savedefconfig), make tips, Graphing the dependencies, Understanding when a full rebuild is necessary, the FAQ Why are some packages not visible, and Adding support for a particular board.
What should you turn off?
One-line answer: Turn off what the product does not need — unused Target packages, heavy init, extra filesystem image formats, and debug-on-target options — not a made-up “flash is small” checklist. Upstream board defconfigs are expected to stay a minimal BusyBox userspace; application packages are product-specific.
Turn-off axes (manual + practice; not a measured SoC footprint):
| Axis | Prefer | Manual context |
|---|---|---|
| Init | BusyBox init (default) over systemd | systemd pulls large deps (dbus, udev, …). BusyBox init is recommended for most embedded systems |
/dev | devtmpfs only, then devtmpfs + mdev | Start with devtmpfs only; add mdev when userspace must react to device events or push firmware |
| Target packages | Anything you do not run on the device | Upstream board configs should not bake in app-specific packages |
| Filesystem images | Only formats you flash/ship | Extra concurrent image types cost build time and disk |
| Toolchain extras | Target-unused C++/locale/RPC-style options (e.g. uClibc knobs) | Toolchain changes often require a full rebuild |
| Debug on target | gdbserver / unstripped extras if the shipping image does not need them | FAQ direction: no compiler, headers, or docs on the target |
Practical order:
- Boot minimum — toolchain · kernel (if needed) · bootloader (if needed) · BusyBox · required rootfs image type(s).
- Init and
/dev— BusyBox init + a light/devpolicy first; systemd/eudev only when required. - Target packages — search with
/in menuconfig, read help, then disable. - Size via graphs — after a clean full build,
make graph-size. Do not invent “our board is N MB.” - Subsystem configs — if you changed BusyBox/kernel, set
BR2_PACKAGE_BUSYBOX_CONFIG/BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILEand save withbusybox-update-config/linux-update-defconfig(Storing the configuration of other components).
Do not: generalize from invented “turning this off saves N KB on board X” numbers. Footprint is a function of libc, strip, selected packages, and image format.
What if dependencies break?
One-line answer: Missing menu entries usually mean unmet dependencies. Disabling a package does not remove its installed files. Dropping an optional dependency does not rebuild reverse dependents. Use graph-depends / show-rdepends, and when unsure run make clean all.
Breakage axes from the manual:
| Symptom | Axis | Mitigation |
|---|---|---|
| Package not in the menu | Unmet deps (including toolchain options) | Search / → enable deps recursively; toolchain option changes ⇒ full rebuild |
| Files remain after OFF | No install-file tracking | Full rebuild to drop them |
| App breaks after library OFF | Optional dep; dependent not rebuilt | *-reconfigure / *-rebuild or make clean all |
| Disabling A also drops B | B selects / depends on A | make <pkg>-show-rdepends / graph-rdepends |
| Flaky partial rebuilds | Arch/toolchain/skeleton change without clean | Manual thumb rule: those changes need a complete rebuild |
Commands that make deps visible:
# Full / per-package dependency graphs (needs graphviz)
make graph-depends
make <pkg>-graph-depends
# Text first-order and recursive deps / reverse deps
make <pkg>-show-depends
make <pkg>-show-recursive-depends
make <pkg>-show-rdepends
make <pkg>-show-recursive-rdepends
# Dump make variables
make -s printvars VARS='BUSYBOX_%DEPENDENCIES'
Working rules:
- Reverse deps before disable — who still needs this package?
- OFF ≠ deleted from disk — Buildroot does nothing special on unselect; clean rebuild for a clean rootfs.
- Optional features — the manual’s openssl/ctorrent example applies in reverse when you remove a library.
- Hidden ≠ removed from the tree — FAQ: packages often hide until toolchain/deps are satisfied.
Bottom line: Minimize while reading the dependency graph, then clean full-rebuild so leftover files do not fool you.
How do you reproduce it?
One-line answer: From a known-good .config, run make savedefconfig to strip options that equal their defaults. Store as configs/<name>_defconfig and reload with make <name>_defconfig. Override the path with BR2_DEFCONFIG=.
From Storing the Buildroot configuration:
make savedefconfig— writes a minimal defconfig (default namedefconfigat the Buildroot source root).- Other path —
make savedefconfig BR2_DEFCONFIG=<path>or setBR2_DEFCONFIGin the configuration. - Recommended location —
configs/<boardname>_defconfigso it appears inmake list-defconfigsand loads viamake <boardname>_defconfig. - Arbitrary path —
make defconfig BR2_DEFCONFIG=<path>. - Kernel/BusyBox/… — save separately with
linux-update-defconfig/busybox-update-config(create the file before pointing Buildroot at it).
Reproduction sketch:
# 1) Trim, then save
make menuconfig # or nconfig / xconfig
make savedefconfig BR2_DEFCONFIG=configs/myproduct_defconfig
# 2) Reload on another tree / in CI
make myproduct_defconfig
# or: make defconfig BR2_DEFCONFIG=/path/to/myproduct_defconfig
# 3) List known defconfigs
make list-defconfigs
# 4) Clean reproducible build
make clean all
Caveats:
- Prefer committing
savedefconfigoutput, not a full noisy.config. - Some in-tree
configs/*_defconfigfiles setBR2_DEFCONFIG; thenmake <board>_defconfigfollowed bysavedefconfigmay overwrite the original. UnsetBR2_DEFCONFIGor passmake savedefconfig BR2_DEFCONFIG=defconfig. - Board support guidance: use fixed versions/commit hashes, not “latest.” Same habit helps product defconfigs.
- Create BusyBox/kernel config files before referencing them, or Buildroot fails when it cannot open the path.
Do not: attach unverified image sizes or prices to a board-named defconfig blurb. This post is about the procedure only.
FAQ
Is savedefconfig the same as linux-savedefconfig?
No. make savedefconfig minimizes the Buildroot .config. linux-savedefconfig / linux-update-defconfig store the kernel config toward BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE (when Linux is enabled).
I disabled packages but the image barely shrank.
Unselect does not always purge output/target. Run make clean all, rebuild, and optionally make graph-size.
Missing from the menu means deleted from the tree?
Usually not. FAQ: packages often hide behind unmet dependencies. Search with /.
What gets lighter if I leave systemd off?
The manual notes systemd brings a fairly large dependency set (dbus, udev, …). Exact megabytes vary — decide whether BusyBox init is enough instead of inventing numbers.
Sources
- Storing the Buildroot configuration —
savedefconfig,BR2_DEFCONFIG,configs/<name>_defconfig - Storing the configuration of other components — linux/busybox/u-boot update helpers
- make tips —
list-defconfigs, when*-menuconfigexists - Graphing the dependencies / package
show-*-depends— deps and reverse deps - Understanding when a full rebuild is necessary — package removal and optional deps
- FAQ: packages not visible — hidden by deps
- Buildroot configuration — init & /dev — BusyBox init, systemd, mdev, eudev guidance
- Adding support for a particular board — minimal BusyBox board defconfig and
savedefconfig