BuildKit and buildx Architecture, Builders, Frontends, Progress, Outputs, and Modern Build Workflows: Guided Hands-On Workflow and Core Operations
Create a disposable docker-container builder, bootstrap and inspect its BuildKit worker, run the same tiny build with plain progress, compare cache behavior, load one result into the local image store, export another as an OCI archive, and preserve build-record evidence before removing only the chapter-owned builder.
Learning objectives
- Inspect the current Buildx/BuildKit environment before creating or selecting any builder.
- Create, bootstrap, and inspect a chapter-owned docker-container builder without replacing shared builders or mutating daemon configuration.
- Run a tiny deterministic build with plain progress and record builder, worker, frontend, cache, and build-record evidence.
- Compare an explicitly loaded image with an OCI archive export and verify the resulting states independently.
- Remove only the disposable builder and chapter-owned image/archive while preserving the evidence packet.
1. Lab scope: one disposable builder, one tiny source tree
This workflow uses a chapter-owned
docker-container builder and a synthetic text-producing
image. It does not publish to a registry, does not mount the Docker
socket into untrusted workloads, and does not modify daemon
configuration. The only persistent artifacts are a small local
image, an OCI archive, build records/cache inside the disposable
builder, and an evidence directory.
2. Preflight: capture current state before creating anything
mkdir -p ch09-lab/evidence
cd ch09-lab
BUILDER="devops-academy-ch09"
IMAGE="devops-academy-ch09:loaded"
printf 'context=%s\n' "$(docker context show)" | tee evidence/00-context.txt
docker version | tee evidence/01-docker-version.txt
docker buildx version | tee evidence/02-buildx-version.txt
docker buildx ls | tee evidence/03-builders-before.txt
docker buildx inspect 2>&1 | tee evidence/04-current-builder-before.txt || true
Do not delete or reconfigure an existing shared builder. If the requested builder name already exists and is not clearly chapter-owned, choose another unique chapter label.
3. Create deterministic synthetic inputs
# syntax=docker/dockerfile:1
FROM busybox:1.37.0
ARG MESSAGE="buildkit-evidence"
RUN printf '%s\n' "$MESSAGE" > /message.txt
CMD ["cat", "/message.txt"]
sha256sum Dockerfile | tee evidence/05-dockerfile-sha256.txt
The ARG value is non-secret synthetic input. Chapter 07
already established that ARG and ENV are
not secret channels.
4. Create and bootstrap an isolated BuildKit builder
docker buildx create \
--name "$BUILDER" \
--driver docker-container
docker buildx inspect --bootstrap "$BUILDER" \
| tee evidence/06-builder-inspect.txt
docker buildx ls | tee evidence/07-builders-after.txt
Inspect the Driver, node endpoint, status, BuildKit version, and platforms. A BuildKit container may be started as part of bootstrap; that container is builder infrastructure owned by the chapter lab.
5. First build: deliberately request no output
docker buildx build \
--builder "$BUILDER" \
--progress=plain \
--build-arg MESSAGE=cache-only \
-t devops-academy-ch09:cache-only \
. 2>&1 | tee evidence/08-cache-only-build.txt
# Expected with docker-container default behavior: this may fail because no
# local image was loaded. Preserve the result rather than treating it as a build failure.
docker image inspect devops-academy-ch09:cache-only \
> evidence/09-local-inspect-cache-only.json 2> evidence/09-local-inspect-cache-only.err || true
6. Second build: request a local Docker image explicitly
docker buildx build \
--builder "$BUILDER" \
--progress=plain \
--load \
--build-arg MESSAGE=loaded-output \
--label devops-academy.lab=ch09 \
-t "$IMAGE" \
. 2>&1 | tee evidence/10-load-build.txt
docker image inspect "$IMAGE" \
--format 'ID={{.Id}} RepoDigests={{json .RepoDigests}}' \
| tee evidence/11-loaded-image.txt
docker run --rm "$IMAGE" | tee evidence/12-runtime.txt
Compare the first and second plain logs. The graph may reuse cached operations, but only the second command asks Buildx to import the result into the Docker image store.
7. Third build: export an OCI image-layout archive
docker buildx build \
--builder "$BUILDER" \
--progress=plain \
--build-arg MESSAGE=oci-output \
--output type=oci,dest=./ch09-result.oci.tar \
. 2>&1 | tee evidence/13-oci-build.txt
sha256sum ch09-result.oci.tar | tee evidence/14-oci-archive-sha256.txt
tar -tf ch09-result.oci.tar | sed -n '1,40p' \
| tee evidence/15-oci-archive-list.txt
The archive is an output artifact. Its SHA-256 identifies the exact tar file, while the OCI manifest/config/layer digests inside identify OCI content. Do not call the archive checksum the image manifest digest.
8. Capture build records and logs
docker buildx --builder "$BUILDER" history ls --no-trunc \
| tee evidence/16-history.txt
docker buildx --builder "$BUILDER" history logs \
| tee evidence/17-last-build-logs.txt
If the installed Buildx does not expose history commands, record that compatibility limitation instead of inventing records. The plain build logs remain valid evidence.
9. Verify the output states independently
| Build | Solve completed? | Local image expected? | OCI file expected? |
|---|---|---|---|
| Cache-only | Yes, if BuildKit completed | No by default | No |
--load |
Yes | Yes | No |
type=oci |
Yes | No unless separately requested | Yes |
10. Small challenge: choose the exporter from the requirement
For each requirement—run the image locally, publish it to a registry, hand an OCI archive to an offline verifier, and export generated static files—choose an exporter/output and name the evidence you would collect after the build. Do not use “green build” as the verification for all four.
11. Exact cleanup
docker image rm "$IMAGE" 2>&1 | tee evidence/18-image-cleanup.txt || true
rm -f ch09-result.oci.tar
docker buildx rm "$BUILDER" \
| tee evidence/19-builder-cleanup.txt
docker buildx ls | tee evidence/20-builders-final.txt
docker buildx rm removes the named builder and its
associated builder state according to the driver. Because this
builder is chapter-owned, that cleanup is bounded. Do not run broad
build-cache or system prune against unrelated builders.
Knowledge check
Why is the first cache-only build useful even though no local image is expected?
It proves solve success and demonstrates that output state is independent. It also seeds/reuses builder cache for later comparison.
What does --load change?
It requests a Docker image exporter/import path so the result becomes available in the local Docker image store.
Why record both Buildx version and
buildx inspect output?
Because the client plugin and BuildKit worker are separately versioned components.
Does deleting builder cache prove why a build failed?
No. It destroys potentially useful evidence and changes performance state before the cause is localized.
What should remain after exact cleanup?
The evidence files you intentionally retained; the chapter-owned builder, image, and OCI archive should be gone.
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.