Skip to content

Build backends

A build backend has one job: produce a root filesystem tree. Everything after that (skeleton merge, .buildignore, required-file generation, validation, squashfs, dm-verity, signing, GPT assembly, metadata) is backend-agnostic and runs identically regardless of which backend produced the rootfs.

Auto-detection

When package.yaml does not set backend:, buildctl probes each registered backend in priority order. The first whose Detect(dir) returns true wins.

Priority Detected from Backend
1 Dockerfile present docker
2 mkosi.conf present mkosi
3 build.sh present shell
4 Makefile present make

Docker beats shell so a project with both (e.g. a multi-stage template) resolves to Docker. Set backend: explicitly to override detection.

Architecture support

Backend Cross-arch Host requirement
Docker yes (QEMU via buildx + binfmt) docker with buildx
mkosi no (fails if --arch does not match host arch) mkosi (Linux)
shell caller responsibility (script receives BUILDCTL_ARCH) bash
make caller responsibility (Makefile receives BUILDCTL_ARCH) make

See Cross-compile for a different architecture for setup.

Common environment

Every backend receives these environment variables, so build scripts and Makefiles do not need to parse package.yaml themselves:

Variable Value
BUILDCTL_NAME name from package.yaml
BUILDCTL_VERSION version from package.yaml
BUILDCTL_ARCH the target architecture (after --arch override)

The shell and make backends also pass the output directory positionally / via a variable. See below.

Docker

Builds a container image with docker buildx build, exports the filesystem via docker export, and returns any OCI image labels as a BuildResult. Cross-arch builds use --platform linux/<arch>.

Source file: Dockerfile. Required dep: docker.

Setup ensures the buildctl-builder buildx builder exists. It does not register binfmt handlers; that is a privileged, one-time operation performed by buildctl init.

The backend extracts the export tarball itself. Path traversal in tar entries is collapsed; absolute symlink targets are rejected; relative symlink and hardlink targets are bounds-checked against the output directory. Character, block, fifo, and socket entries are skipped.

backend_arguments

Key Type Notes
build_args map Each entry is passed as --build-arg <key>=<value>. Keys are sorted for deterministic command lines.
dockerfile string Alternative Dockerfile path, relative to the project directory. Passed via --file.
target string Multi-stage build target. Passed via --target.

Unknown keys are silently ignored.

mkosi

Builds a filesystem tree from distribution packages with mkosi. Output is forced to Format=directory; if mkosi.conf sets a different Format=, the build fails.

Source file: mkosi.conf. Required dep: mkosi (Linux only).

Rejects cross-arch requests; if --arch does not match runtime.GOARCH, it returns an error pointing at the Docker backend.

backend_arguments

Key Type Notes
extra_args string list Each entry is appended verbatim to the mkosi command line.

shell

Runs build.sh under bash -eu -o pipefail. The output directory is passed as $1. The script is expected to populate $1 with a rootfs tree.

Source file: build.sh. Required dep: bash. backend_arguments is ignored.

The scaffold template (buildctl new --backend shell) installs a sample binary and documents available environment variables in a comment block.

make

Runs make OUTDIR=<outDir> in the project directory. The Makefile is expected to populate OUTDIR with a rootfs tree.

Source file: Makefile. Required dep: make.

backend_arguments

Key Type Notes
target string Makefile target to invoke. Defaults to none (the first target).
extra_args string list Each entry is appended verbatim to the make command line.