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):

AxisPreferManual context
InitBusyBox init (default) over systemdsystemd pulls large deps (dbus, udev, …). BusyBox init is recommended for most embedded systems
/devdevtmpfs only, then devtmpfs + mdevStart with devtmpfs only; add mdev when userspace must react to device events or push firmware
Target packagesAnything you do not run on the deviceUpstream board configs should not bake in app-specific packages
Filesystem imagesOnly formats you flash/shipExtra concurrent image types cost build time and disk
Toolchain extrasTarget-unused C++/locale/RPC-style options (e.g. uClibc knobs)Toolchain changes often require a full rebuild
Debug on targetgdbserver / unstripped extras if the shipping image does not need themFAQ direction: no compiler, headers, or docs on the target

Practical order:

  1. Boot minimum — toolchain · kernel (if needed) · bootloader (if needed) · BusyBox · required rootfs image type(s).
  2. Init and /dev — BusyBox init + a light /dev policy first; systemd/eudev only when required.
  3. Target packages — search with / in menuconfig, read help, then disable.
  4. Size via graphs — after a clean full build, make graph-size. Do not invent “our board is N MB.”
  5. Subsystem configs — if you changed BusyBox/kernel, set BR2_PACKAGE_BUSYBOX_CONFIG / BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE and save with busybox-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:

SymptomAxisMitigation
Package not in the menuUnmet deps (including toolchain options)Search / → enable deps recursively; toolchain option changes ⇒ full rebuild
Files remain after OFFNo install-file trackingFull rebuild to drop them
App breaks after library OFFOptional dep; dependent not rebuilt*-reconfigure / *-rebuild or make clean all
Disabling A also drops BB selects / depends on Amake <pkg>-show-rdepends / graph-rdepends
Flaky partial rebuildsArch/toolchain/skeleton change without cleanManual 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:

  1. Reverse deps before disable — who still needs this package?
  2. OFF ≠ deleted from disk — Buildroot does nothing special on unselect; clean rebuild for a clean rootfs.
  3. Optional features — the manual’s openssl/ctorrent example applies in reverse when you remove a library.
  4. 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:

  1. make savedefconfig — writes a minimal defconfig (default name defconfig at the Buildroot source root).
  2. Other path — make savedefconfig BR2_DEFCONFIG=<path> or set BR2_DEFCONFIG in the configuration.
  3. Recommended location — configs/<boardname>_defconfig so it appears in make list-defconfigs and loads via make <boardname>_defconfig.
  4. Arbitrary path — make defconfig BR2_DEFCONFIG=<path>.
  5. 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:

  1. Prefer committing savedefconfig output, not a full noisy .config.
  2. Some in-tree configs/*_defconfig files set BR2_DEFCONFIG; then make <board>_defconfig followed by savedefconfig may overwrite the original. Unset BR2_DEFCONFIG or pass make savedefconfig BR2_DEFCONFIG=defconfig.
  3. Board support guidance: use fixed versions/commit hashes, not “latest.” Same habit helps product defconfigs.
  4. 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