Docker Buildx Bake: Target Inheritance, Matrices, and Resolved Plans
Review Docker Buildx Bake inheritance, matrix expansion, merged files, and --print output before parallel builds alter a release plan.
Docker Buildx Bake turns a build definition into one or more BuildKit targets. Its value is not merely shorter command lines: it gives a release pipeline a declarative plan that can be reviewed before a builder executes it. The risk is that the plan can be assembled from several files, inherit attributes, and expand a matrix into more targets than the short invocation suggests.
Bake describes build configuration. It does not, by itself, prove that an image was built, exported, or published. Keep those as separate checks in a release pipeline.
Start with the target graph, not the command line
A Bake target corresponds to one build invocation. A group collects targets, and invoking a group asks Bake to build its members. This distinction matters when reading CI logs: a successful group can represent several independently configured targets, while a target’s target attribute selects a stage inside its Dockerfile. The outer Bake target name and the inner Dockerfile stage are different identifiers.
The following HCL fragment generates two test builds from one target. It assumes the repository’s Dockerfile has unit and integration stages:
target "_test-base" {
context = "."
dockerfile = "Dockerfile"
}
target "checks" {
inherits = ["_test-base"]
name = "checks-${stage}"
matrix = {
stage = ["unit", "integration"]
}
target = stage
}
The matrix creates one target for each value, named checks-unit and checks-integration. Every generated name must be unique. With multiple matrix axes Bake creates every combination, so a two-by-three matrix produces six targets; a careless naming expression can make those variants collide. The matrix values also need to select real Dockerfile stages or otherwise match the target’s intended build configuration.
Inheritance reduces repeated configuration, but it makes ordering part of the review. When multiple parents define a conflicting attribute, the later parent in the inherits list takes precedence. Make release-specific overrides visible and keep the parent list short enough that a reviewer can calculate the effective target without reconstructing a large inheritance tree.
Resolve every input file explicitly
When no file is specified, Bake searches for supported Compose and Bake filenames in a documented order. If it finds more than one, it loads and merges them. The merge is attribute-specific: some duplicate values are replaced by the later definition (including tags, platforms, output, and target), while other values are combined. A developer who reviews only docker-bake.hcl can therefore miss a tag or output changed by a Compose file loaded first or an override file loaded later.
For a release job, make the intended inputs visible in the command. If the plan intentionally combines files, list each one and inspect the resolved result:
docker buildx bake -f compose.yaml -f docker-bake.hcl --print checks
docker buildx bake -f compose.yaml -f docker-bake.hcl --print release
--print emits the resulting target options as JSON without starting a build. Review the target names, Dockerfile stages, contexts, platforms, tags, outputs, and any privileged entitlements in that output. Treat it as a configuration review, not as evidence that a registry contains the resulting artifact. The subsequent build still needs its own status, digest, and publication checks.
This is especially important for outputs. A matrix can be harmless when it runs validation stages and risky when every generated target writes to the same destination or publishes a mutable tag. Give artifacts distinct names where appropriate, decide which target owns publication, and avoid attaching release outputs to a broad validation group by accident.
Bound concurrency and failure behavior
Bake can execute targets in parallel. That can shorten a build but also increases simultaneous CPU, memory, network, and registry demand. Set a deliberate --jobs limit for constrained runners; its concurrency controls Bake targets, not the internal parallelism of steps inside one BuildKit solve.
Failure mode changes what a pipeline may observe. The default fail-fast mode cancels targets that are still running after a failure, but outputs already written by successful targets are not removed. defer-error lets independent targets continue and still returns an error after they finish. defer-output waits for participating target solves to reach their export boundary before beginning exports, but exports are not a transaction: an export failure can still leave partial output, and completed exports are not rolled back. Choose the mode based on artifact semantics, and make downstream publication conditional on the entire Bake invocation succeeding.
A review checklist for release jobs
Before changing a production Bake invocation, record the Buildx version and builder selected by the job, then inspect the exact files and variables supplied by CI. Run --print with the same target/group and environment as the real build. Compare the resolved JSON with the intended Dockerfile stages, contexts, platform list, tags, and output destinations. Check that matrix-generated names are distinct and that no inherited or merged attribute silently replaces a release value.
Then run the build with an explicit concurrency policy. Capture the command’s exit status and per-target metadata; verify the expected digest in the actual destination rather than inferring publication from a green build step. Test a failing target and a failing export in a disposable repository or registry so that partial-output behavior is understood before a production incident.
Bake is most useful when its resolved plan is treated as a reviewable interface between source configuration and the builder. Explicit inputs, unique matrix targets, visible inheritance, and a separate artifact-verification step turn a compact HCL file into a release plan that operators can actually audit.
Related:
- BuildKit Cache Strategy for CI: Layers, Mounts, and Trust Boundaries
- OCI Image Indexes: How One Container Tag Selects Multiple Platforms
Sources: