Chapter 09Lesson 01~100 minutes

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.

BuildKitBuildxBuildersLLBExporters

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.
Chapter 09 evidence baseline — verified 2026-09-21. Mandatory exercises use a synthetic local source tree and a uniquely named disposable docker-container builder; they do not require a registry, paid cloud builder, Kubernetes cluster, production daemon, or privileged shared CI runner. At verification time Docker Buildx 0.37.1 and BuildKit 0.33.0 are the current upstream releases; BuildKit 0.33.0 includes the built-in Dockerfile frontend 1.27.0, while Docker Engine 29.8.1 remains the current Engine 29 course baseline. Docker documents the docker driver as Engine-integrated with automatic local image loading, while docker-container/remote/Kubernetes-style drivers keep an unspecified-output result in BuildKit cache unless an exporter such as --load, --push, or --output is selected. Every lab therefore records the actual local Buildx/BuildKit/frontend/worker state and verifies output destination independently.

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

BuildKit/buildx ownership chain
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.

Practical rule: source context + Dockerfile text + frontend + builder/worker + external inputs + exporter settings are all build inputs or execution assumptions. Preserve them with the result.

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

Client/builder

Docker/Buildx versions, active context, builder name, driver, node endpoint.

Worker/frontend

BuildKit version, worker platforms, frontend syntax/version, entitlements.

Execution

Build record ID, plain progress/logs, cache hits/misses, source/context identity.

Output

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.

Next lesson

Next: Guided Hands-On Workflow and Core Operations

Create an isolated builder and prove how execution, cache, local load, and OCI export differ.

Knowledge check

Does docker buildx version prove which BuildKit daemon executed a build?

Why can a successful docker-container build produce no local Docker image?

What does the Dockerfile frontend do?

Is a cache hit the same as an immutable release digest?

Which layer owns the choice between local image store and OCI archive?

Official references and version notes

Version and compatibility note

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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.