BuildKit and buildx Architecture, Builders, Frontends, Progress, Outputs, and Modern Build Workflows: Concepts, Architecture, and Mental Model
Buildx is the Docker CLI surface for advanced BuildKit workflows, but a successful build is not automatically a locally loaded image. This lesson separates the Buildx client, builder instance, driver, Dockerfile frontend, LLB graph, BuildKit worker/cache, and exporter so every build state has an owner and observable evidence.
Learning objectives
- Trace a build request from Docker CLI/Buildx through a selected builder and driver, Dockerfile frontend, LLB graph, BuildKit worker/cache, and exporter.
- Distinguish Buildx version, BuildKit daemon version, Dockerfile frontend version, worker platform, builder name, driver, and build record rather than treating them as one versioned component.
- Explain why the docker driver normally loads a single-platform image to the local image store while docker-container and other drivers require an explicit output such as --load, --push, or --output.
- Identify which evidence proves build execution, cache reuse, local-image availability, OCI/tar output, or registry publication.
- Relate builder isolation, cache ownership, immutable output identity, and exporter choice to secure reproducible DevOps delivery.
1. The problem: “the build succeeded” is not a complete state
Chapter 08 bounded the source inputs a builder may read. Chapter 09 follows those inputs through the build execution system. The phrase “Docker built my image” hides several independent decisions: which Buildx client version submitted the request, which builder instance was selected, which driver owns that builder, which Dockerfile frontend translated the Dockerfile, which BuildKit worker executed the graph, which cache records were reused, and which exporter received the result.
A completed BuildKit solve can therefore be real even when
docker image ls shows no new image. With a non-default
driver such as docker-container, a result can remain
only in BuildKit cache until an exporter such as
--load, --push, or
--output is requested. Operationally, execution success
and output availability are different states.
2. Mental model: request → graph → worker → exporter
flowchart TD
A[Docker CLI / buildx] --> B[Selected builder instance]
B --> C[Driver: docker / docker-container / remote / kubernetes / cloud]
C --> D[BuildKit daemon + worker]
A --> E[Dockerfile frontend]
E --> F[LLB build graph]
F --> D
D --> G[Content + result cache]
D --> H[Exporter]
H --> I[Docker image store]
H --> J[Registry]
H --> K[OCI/Docker archive]
H --> L[Local filesystem / tar]
Buildx is the Docker CLI plugin/orchestration layer. A builder instance is a named configuration that can contain one or more nodes. A driver decides how BuildKit runs. A frontend converts a high-level build definition such as a Dockerfile into BuildKit's lower-level build graph. A worker executes that graph. An exporter decides where the result goes.
3. Separate the versions before debugging
| Evidence | What it identifies | Do not confuse it with |
|---|---|---|
docker buildx version |
Buildx client/plugin release | BuildKit daemon version |
docker buildx inspect --bootstrap NAME |
Builder driver, nodes, worker status/platforms, often BuildKit version | Docker Engine version |
# syntax=... |
Requested Dockerfile frontend | Buildx version |
docker version |
Docker client/server/API identity | Selected builder identity |
| Build record ID | One completed/failed build execution | Final image digest |
| Exporter result/digest | Output identity/destination | Cache key or local image ID in every backend |
4. The driver chooses where BuildKit runs
The default docker driver uses BuildKit components
integrated with Docker Engine and prioritizes convenience. It
normally loads compatible image results into the local image store
automatically. The docker-container driver runs a
dedicated BuildKit container and is more configurable; it supports
advanced exporters and multi-platform workflows, but it does not
automatically put a successful result into the local Docker image
store unless an output is requested.
Remote, Kubernetes, and cloud drivers move the worker boundary elsewhere. That changes trust, network, storage, cache ownership, platform capacity, and operational responsibility even if the Dockerfile is identical.
5. Frontend and LLB: the Dockerfile is translated before workers execute it
The Dockerfile frontend parses Dockerfile syntax and produces an LLB graph: a content-addressed, dependency-aware representation BuildKit can schedule and cache. The frontend is therefore part of the build toolchain. If a newer Dockerfile feature works on one builder but not another, first record the effective frontend and BuildKit versions instead of assuming the Dockerfile text alone determines behavior.
6. Cache is execution evidence, not release identity
BuildKit's cache allows graph operations to be reused when their
inputs match. A CACHED progress line means BuildKit
reused a prior result for that operation under the current cache
model; it does not prove that the final image was loaded, pushed,
signed, scanned, or deployed. Cache can accelerate reproducible
builds, but it must remain separate from the immutable identity of a
released image.
7. Exporters define output state
| Exporter/output | Typical result | Verification |
|---|---|---|
--load / Docker exporter |
Single-platform image imported into local Docker image store | docker image inspect |
--push / registry exporter |
Image content published to a registry | Registry digest / pull / imagetools inspection |
type=oci |
OCI image-layout archive | Archive checksum and OCI layout contents |
type=local |
Build filesystem output written to client path | File hashes at destination |
| No explicit output on non-auto-load driver | Result retained only in BuildKit cache | Build record/cache evidence; local image absence is expected |
8. Progress is a diagnostic surface
--progress=plain is useful in teaching and CI because
it produces durable, line-oriented step evidence. Interactive
tty is easier to read live. rawjson is
suitable for machine processing. A progress mode changes
presentation, not the semantic build graph.
docker buildx ls
docker buildx version
docker buildx inspect --bootstrap
docker buildx build --progress=plain .
Run the last command only against a disposable source tree. If the selected builder does not auto-load results, lack of a local image is not proof that the solve failed.
9. Evidence map for one build
Docker/Buildx versions, active context, builder name, driver, node endpoint.
BuildKit version, worker platforms, frontend syntax/version, entitlements.
Build record ID, plain progress/logs, cache hits/misses, source/context identity.
Exporter type, local image ID/digest, registry digest, archive checksum, or explicit cache-only state.
10. Small challenge: local image missing after a green build
A colleague uses a docker-container builder. BuildKit
prints DONE, but
docker image inspect team/app:test says the image does
not exist. Name the first three pieces of evidence you would
capture, and explain why adding --load can be the
correct fix while deleting the builder cache is not.
Knowledge check
Does docker buildx version prove which BuildKit
daemon executed a build?
No. It identifies the Buildx client/plugin. Inspect the selected builder and its node(s) to identify BuildKit.
Why can a successful docker-container build produce no local Docker image?
Because that driver does not auto-load by default. Without an exporter, the result can remain only in BuildKit cache.
What does the Dockerfile frontend do?
It parses the Dockerfile/build definition and translates it into the lower-level build graph BuildKit executes.
Is a cache hit the same as an immutable release digest?
No. Cache is an execution optimization. Release identity must be verified from the exported image/artifact digest.
Which layer owns the choice between local image store and OCI archive?
The exporter/output configuration, not the Dockerfile instruction graph itself.
Official references and version notes
- Builders — Buildx builder instances, nodes, drivers, and builder selection.
-
Build drivers
— current
docker,docker-container,cloud,kubernetes, andremotedriver behavior and capabilities. - Docker container driver — isolated BuildKit container lifecycle, driver options, and explicit output behavior.
- Docker Buildx CLI — builder override, subcommands, and current Buildx surface.
-
docker buildx build— progress modes, exporters, metadata,--load,--push, and output semantics. -
docker buildx inspect— builder/driver/nodes/status/platform and bootstrap evidence. - Build history — recorded build IDs, status, time, duration, logs, and record inspection.
- Exporters — image, registry, local, tar, OCI, and Docker build-result destinations.
- OCI and Docker exporters — OCI/Docker archive semantics and compatible drivers.
- Dockerfile reference — Dockerfile frontend syntax and build instruction semantics.
- Buildx releases — upstream Buildx release history.
- BuildKit releases — BuildKit and built-in Dockerfile frontend release history.
- Docker Engine 29 release notes — current Engine baseline and bundled component updates.
Version-sensitive statements were rechecked against primary sources on 2026-09-21. Buildx 0.37.1 is the current upstream Buildx release (2026-09-11), BuildKit 0.33.0 is the current upstream BuildKit release (2026-09-02), and that BuildKit release updates the built-in Dockerfile frontend to 1.27.0. Docker Engine 29.8.1 remains the current Engine 29 patch baseline used by this course at verification time. Tooling is independently versioned, so every lab captures the learner's actual Engine/CLI/Buildx/BuildKit/frontend/worker state instead of assuming these versions.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.