Create your own
Lesson illustration

Buildroot Package Directory Mapping: Download to Image Output

Hello. In the previous lesson, you configured and built a QEMU system with Buildroot, then proved that its kernel, root filesystem, console, and BusyBox init formed a working boot contract. You also saw the key output areas: host, staging, target, and images.

This lesson begins the investigative part of the Buildroot prototype module. You will treat a package build as a traceable supply path, locating its source input, unpacked build tree, build-time tools and sysroot, runtime files, and final deployable image. This is a necessary habit for later work on custom packages, patches, boot failures, and product release evidence.

The important correction to make at the start is this: a Buildroot package does not necessarily leave a file in every directory. A target application such as BusyBox normally ends up in target and then the root filesystem image, but it may not install development files into staging. A host-only helper tool belongs in host and should never reach the target image. The directories describe roles in the integration system, not a mandatory seven-step physical path for every package.


One Buildroot build, several file populations

Keep the following categories distinct:

LocationWhat it containsPrimary consumerShould it be deployed?
Download directory, usually dl/ or BR2_DL_DIRRetrieved source archives, source snapshots, and sometimes cached Git materialBuildroot fetch and extract logicNo
output/build/Per-component source trees, object files, generated files, build logs, and stampsThe package build itselfNo
output/host/Programs that execute on Ubuntu, including Buildroot helper tools and cross-toolchain componentsBuildroot and package build commandsNo
output/staging/Target headers, unversioned library links, and target development librariesCompilation and linking of later target packagesNo
output/target/A runtime-oriented target root filesystem treeRoot filesystem image generationNot directly
output/images/Bootable and flashable outputs: root filesystem images, kernel, bootloader, DTBs, and related artifactsQEMU, bootloader, flashing process, release pipelineYes

A useful way to think about the boundaries is:

  1. The download directory records an input obtained from outside the build.
  2. The build directory is the package’s private construction site.
  3. The host directory provides tools that run on the development workstation.
  4. The staging directory provides target development interfaces to other packages.
  5. The target directory receives files needed at runtime.
  6. The images directory contains finalized deployable representations.

The same source project can appear in more than one role. For example, Buildroot may build host-pkgconf because the host needs pkgconf during compilation, while it may separately build a target-side pkgconf if the final device needs that command. Those are different package variants with different installation destinations.

Buildroot - Usage and documentation

Read this Buildroot usage reference for its concise distinction between build, host, staging, target, and images directories. Its terminology is stable across Buildroot releases, though exact subdirectory names can vary with the configured architecture and toolchain.

In the section describing Buildroot output, read the directory breakdown. Focus especially on why staging contains development material, why target is only an intermediate filesystem tree, and why the final bootable output is under images.

The two meanings of “host”

This terminology can initially be confusing:

  • Your host machine is the Ubuntu laptop on which Buildroot runs.
  • output/host/ contains binaries that execute on that host machine.
  • A target package is compiled for QEMU’s emulated architecture, not for Ubuntu.
  • output/staging/ contains target-architecture headers and libraries used while compiling target packages.

With an external toolchain, output/host/ commonly includes an imported or wrapped cross-toolchain. Modern Buildroot installations also commonly make output/staging a symbolic link into a target sysroot beneath output/host/. Do not treat that symlink as proof that staging files are host binaries: inspect an ELF file if you are unsure of its architecture.

For example, a header at:

output/staging/usr/include/zlib.h

is not executable at all. It is a target development interface. A library such as:

output/staging/usr/lib/libz.so

is often an unversioned linker name used during compilation. The corresponding runtime object, when the package is selected for the target, is more likely to look like:

output/target/usr/lib/libz.so.1

The unversioned .so and headers are usually omitted from a production root filesystem because they are development material. This separation is exactly what prevents a target image from silently turning into a development SDK.


Read the package pipeline as evidence, not as a black box

Buildroot describes package work through logical targets such as source, extract, patch, configure, build, install-staging, and install-target. Internally, completion is represented by stamp files in the package’s build directory.

Untitled :: Buildroot

Read the relevant parts of Bootlin’s Buildroot Common Usage guide. It explains both the persistent source-cache role of BR2_DL_DIR and the package build stages that leave evidence in the output tree.

First, under “Location of downloaded packages,” read the download-cache discussion. Notice that an environment setting can override the value recorded in .config. Then, under “Package-specific make targets,” read the ordered package stages. Relate install-staging to build-time target development files, install-target to runtime files, and the host-package install step to output/host.

The stage names do not guarantee that every stage performs work:

  • A target application may only perform install-target.
  • A target library commonly performs both install-staging and install-target.
  • A host package installs into output/host; it has no target runtime installation.
  • A kernel, bootloader, or device tree can be built in output/build and copied directly to output/images, without becoming a normal file in output/target.
  • A locally overridden source tree can deliberately bypass the normal download and extraction path.

This is why “Where is package X?” is not a single-directory question. The better question is: Which package role is this, and which outputs should that role produce?


Establish a directory map for your existing QEMU build

Reuse the variables from the previous lesson. If you started a new terminal, redefine the paths to match your environment:

export BR="$HOME/work/buildroot"
export LAB="$HOME/work/buildroot-qemu-lab"
export CACHE="$LAB/source-cache"
export OUT="$HOME/build-output/qemu-external"

First, confirm which directories actually exist and whether staging is a symbolic link:

for d in build host staging target images; do
    printf '\n%-8s ' "$d"
    if test -e "$OUT/$d" || test -L "$OUT/$d"; then
        readlink -f "$OUT/$d"
        du -sh "$OUT/$d"
    else
        echo "not present"
    fi
done

ls -ld "$OUT/staging"

A typical result shows:

  • many package-specific subdirectories under build;
  • a substantial host tree;
  • staging resolving into a sysroot associated with the target tuple;
  • a smaller target tree;
  • a compact images directory containing the kernel and filesystem artifacts.

Now inventory the component build directories:

find "$OUT/build" -mindepth 1 -maxdepth 1 -type d \
    -printf '%f\n' | sort | sed -n '1,100p'

You will usually see a mixture of names such as:

busybox-<version>
linux-<version>
host-fakeroot-<version>
host-mkpasswd-<version>

The host- prefix is meaningful. It usually identifies a package variant that Buildroot built to execute on your Ubuntu workstation during the build. It does not mean the package is intended for the target.

Inspect a few executables from host:

find "$OUT/host/bin" -maxdepth 1 \
    \( -type f -o -type l \) -printf '%f\n' | sort | sed -n '1,80p'

find "$OUT/host" -type d -path '*/sysroot/usr/include' -print

The first command lists utilities Buildroot may invoke while building. The second helps locate the target sysroot associated with the external cross-toolchain. The exact directory containing the sysroot depends on your target tuple and Buildroot release.


A concrete trace: BusyBox from source cache to image

BusyBox is a good first trace because it is present in the minimal QEMU system and you can see its runtime result clearly. It is also useful precisely because it demonstrates that a target package need not populate staging.

Set the package name and locate its build directory:

PKG="busybox"

PKGDIR="$(find "$OUT/build" -mindepth 1 -maxdepth 1 -type d \
    -name "${PKG}-*" -print | head -n 1)"

printf 'Package build directory: %s\n' "$PKGDIR"

If this prints an empty path, do not guess at a version number. Re-run the directory inventory and confirm that the supplied configuration enabled BusyBox:

grep '^BR2_PACKAGE_BUSYBOX=' "$OUT/.config" || true

Once PKGDIR is valid, inspect its stamps and top-level contents:

find "$PKGDIR" -maxdepth 1 -type f -name '.stamp_*' \
    -printf '%f\n' | sort

find "$PKGDIR" -maxdepth 1 -mindepth 1 \
    -printf '%y %f\n' | sort | sed -n '1,80p'

Depending on the Buildroot release and package infrastructure, stamp names commonly indicate completed download, extraction, patching, configuration, build, and installation work. These stamps are Buildroot’s local evidence that it does not need to repeat a completed step.

Locate the source input

Inspect the BusyBox package metadata in the Buildroot source tree:

grep -nE '^(BUSYBOX_VERSION|BUSYBOX_SOURCE|BUSYBOX_SITE)' \
    "$BR/package/busybox/busybox.mk"

Then look for the cached source archive. Use your lab’s approved cache path, replacing "$CACHE" if the resolved BR2_DL_DIR setting identifies another location:

find "$CACHE" -type f -iname 'busybox*' -printf '%f\n' | sort

The package metadata tells Buildroot what version and source location it expects. The download cache contains the retrieved input. The extracted source tree and all compilation products belong under output/build/busybox-<version>.

This is an important provenance boundary:

  • package/busybox/busybox.mk says what Buildroot intends to obtain.
  • The download cache contains the fetched source input.
  • The build directory contains the particular unpacked and built instance.
  • The configuration and Buildroot revision tell you which metadata rules governed the build.

A valid cache is not merely a performance optimization. Along with the Buildroot revision and configuration, it is part of the evidence needed to recreate an offline build using the same input versions.

Locate the runtime installation

BusyBox normally installs its multicall executable and applet links into the target tree:

find "$OUT/target" -type f -name busybox -printf '%p\n'

for f in "$OUT/target/bin/sh" "$OUT/target/sbin/init"; do
    if test -e "$f" || test -L "$f"; then
        printf '%s: ' "$f"
        readlink -f "$f"
    fi
done

file "$OUT/target/bin/busybox" 2>/dev/null || true

The expected relationship is that /bin/sh and /sbin/init resolve to BusyBox or are BusyBox applet links. Their presence in target means Buildroot has prepared them for the runtime filesystem.

Now deliberately check the staging tree:

find "$OUT/staging" -type f -name busybox -printf '%p\n' 2>/dev/null

An empty result is normally correct for BusyBox. BusyBox is primarily a runtime program in this minimal configuration; other target packages generally do not compile by including BusyBox headers or linking to a BusyBox library.

So the BusyBox trace is:

Evidence pointExpected BusyBox result
Download cacheBusyBox source archive or another configured source form
output/build/busybox-<version> source and build tree, with stamps
output/host/Build tools may be used, but BusyBox itself is not installed as a host utility by this target-package build
output/staging/Usually no BusyBox development installation
output/target//bin/busybox plus shell, init, and other applet links
output/images/The final root filesystem image contains the runtime BusyBox files

This is a complete package map even though two locations have an intentional absence.


Trace a target library and observe staging versus target

A library package is the clearest way to see why Buildroot has both staging and target trees. First, list actual shared libraries in your generated runtime filesystem:

find "$OUT/target" -type f -name '*.so.*' \
    -printf '%p\n' | sort | sed -n '1,80p'

find "$OUT/staging/usr/include" -type f \
    -printf '%p\n' 2>/dev/null | sort | sed -n '1,40p'

Do not select the C library as your first example if you are using an external toolchain. Its headers and libraries may have arrived as part of the external-toolchain import rather than as a separately selected Buildroot target package.

Instead, choose an identifiable optional library that appears in both locations, if your configuration includes one. For a package called foo, the expected pattern is:

TreeTypical evidence for a target library
Download cachefoo-<version> source archive, Git snapshot, or configured source material
build/foo-<version> build directory and stamps
host/Cross-compiler and host build utilities used to compile foo
staging/foo.h, libfoo.so, static archives, pkg-config metadata
target/Versioned runtime library such as libfoo.so.1 and runtime configuration, if required
images/The runtime library inside the chosen root filesystem image

The unversioned libfoo.so is usually a development linker interface. The versioned libfoo.so.1 is normally what a dynamically linked target program loads at runtime. This distinction later becomes central when packaging versioned libraries and diagnosing missing shared-library failures.

Confirm the final image rather than trusting target

Buildroot’s target directory is close to the final root filesystem but is not itself the deployable artifact. Image generation applies filesystem-specific ownership, permissions, device-node rules, compression, and layout.

List your image outputs:

find "$OUT/images" -maxdepth 1 -type f \
    -printf '%f\t%s bytes\n' | sort

find "$OUT/images" -maxdepth 1 -type f -name 'rootfs.*' \
    -exec file {} \;

If your image format is a tar archive, inspect it without extracting:

tar -tf "$OUT/images/rootfs.tar" | grep -E '(^|/)busybox$|(^|/)bin/sh$|(^|/)sbin/init$'

If your output is SquashFS and Buildroot produced unsquashfs, inspect its listing read-only:

"$OUT/host/bin/unsquashfs" -l "$OUT/images/rootfs.squashfs" \
    2>/dev/null | grep -E '/(busybox|bin/sh|sbin/init)$'

Use only the command matching an image file that actually exists. Do not mount the image merely to inspect it; mounting adds unnecessary privilege and cleanup concerns. A file listing is sufficient to prove that an expected runtime path made it into the final artifact.

For ext2 or ext4 output, debugfs can inspect the filesystem image without mounting it if it is available in the Buildroot host tools:

DEBUGFS="$(find "$OUT/host" -type f -name debugfs -print | head -n 1)"
printf 'debugfs: %s\n' "$DEBUGFS"

The final image is the artifact you hash, boot in QEMU, flash to a board, and eventually release. The target tree is evidence about what Buildroot intends to put there, but images is the final deployment boundary.


Use the map for diagnosis and controlled rebuilds

Two practical rules follow from this layout.

First, Buildroot does not maintain a general reverse database saying which package owns every file in output/staging and output/target. Therefore, removing a package or manually deleting its files from those shared trees is unsupported. A file found in target is evidence, but authoritative diagnosis requires examining the relevant package metadata, its build directory, stamps, and installation commands.

Second, output/build is disposable integration state. Do not develop by editing a package’s extracted source tree and expecting the change to survive cleanup. The build directory can be removed by make clean, and Buildroot can re-extract the source. Later, when you develop a gateway application, you will keep its source under Git and use a proper package definition or Buildroot’s source-override mechanism.

For now, retain a compact directory-map record with your existing build evidence:

mkdir -p "$LAB/evidence"

{
    echo "Buildroot revision:"
    git -C "$BR" rev-parse HEAD 2>/dev/null || true
    echo
    echo "Resolved download setting:"
    grep '^BR2_DL_DIR=' "$OUT/.config" || true
    echo
    echo "BusyBox build directory:"
    printf '%s\n' "$PKGDIR"
    echo
    echo "BusyBox stamps:"
    find "$PKGDIR" -maxdepth 1 -type f -name '.stamp_*' \
        -printf '%f\n' | sort
    echo
    echo "Deployable images:"
    find "$OUT/images" -maxdepth 1 -type f \
        -printf '%f\t%s bytes\n' | sort
} > "$LAB/evidence/buildroot-package-map.txt"

find "$OUT/images" -maxdepth 1 -type f -print0 |
    sort -z |
    xargs -0 sha256sum > "$LAB/evidence/qemu-images.sha256"

This record is small, but it gives you a defensible answer to several real engineering questions: which Buildroot revision was used, what source cache was intended, where BusyBox was built, which stages completed, and precisely which image artifacts were produced.


You can now distinguish Buildroot’s six important storage roles: source inputs in the download cache, isolated per-component construction in build, host-executed tooling in host, target development interfaces in staging, runtime content in target, and deployable output in images.

The key takeaway is that package tracing is role-aware. BusyBox reaches the runtime tree but normally not staging; a target library commonly reaches both staging and target; a host helper reaches only host; and a kernel often goes straight from its build tree to images. This prevents several common errors: copying staging into an image, deploying target directly, treating host tools as target binaries, or deleting shared-tree files to remove a package.

Next, you will customize the Buildroot root filesystem with a skeleton overlay and a deterministic post-build script. The directory map from this lesson will let you verify exactly where that customization enters the build and how it appears in the final image.

Can't find a good explanation? Sign up and we'll make it for you

Sign up