Chapter 36Lesson 05~230 minutes

Checkpoint Lab — Docker in CI/CD: Reproducible Builds, Buildx, Registry Caching, Ephemeral Runners, and Release Promotion

Execute a digest-bound synthetic CI/CD checkpoint that builds once, tests once, records evidence, promotes the same digest, and proves no second build occurred.

CheckpointBuild oncePromote digestEvidence packetChapter 37 bridge

Learning objectives

  • Execute a synthetic source-to-registry pipeline that produces one immutable release subject.
  • Capture source, builder, cache, test, digest and attestation evidence before promotion.
  • Promote the same subject under a release alias without invoking a second build.
  • Verify downstream identity independently and document any local-registry simulation limitations.
  • Produce a concise evidence packet proving “build once, promote same bytes.”

1. Checkpoint contract

The checkpoint is successful only if you can show one build invocation produced the candidate, tests were run against that candidate, publication produced an immutable digest, the release alias points to the same digest, and no second Dockerfile build occurred during promotion. Use the local registry path if supported; otherwise perform the OCI-layout simulation and mark registry-referrer evidence as simulated.

2. Workspace, versions and run identity

set -eu
LAB=dca36-checkpoint
RUN_ID="local-$(date -u +%Y%m%dT%H%M%SZ)"
mkdir -p "$LAB/src" "$LAB/evidence" "$LAB/cache"
printf '%s
' "$RUN_ID" | tee "$LAB/evidence/run-id.txt"
docker context show | tee "$LAB/evidence/context.txt"
docker version | tee "$LAB/evidence/docker-version.txt"
docker compose version | tee "$LAB/evidence/compose-version.txt" || true
docker buildx version | tee "$LAB/evidence/buildx-version.txt"

3. Create source and record revision

cat > dca36-checkpoint/src/Dockerfile <<'EOF'
# syntax=docker/dockerfile:1
FROM alpine:3.22
ARG SOURCE_SHA
LABEL org.opencontainers.image.revision=$SOURCE_SHA
RUN printf 'source=%s
' "$SOURCE_SHA" > /release.txt
CMD ["cat","/release.txt"]
EOF
SOURCE_SHA=$(git rev-parse HEAD 2>/dev/null || sha256sum dca36-checkpoint/src/Dockerfile | cut -d' ' -f1)
printf '%s
' "$SOURCE_SHA" | tee dca36-checkpoint/evidence/source-sha.txt
sha256sum dca36-checkpoint/src/Dockerfile | tee dca36-checkpoint/evidence/dockerfile.sha256

4. Predict state changes before execution

Prediction 1: a new builder named dca36-release will own checkpoint BuildKit state.
Prediction 2: exactly one Dockerfile build invocation will create the candidate.
Prediction 3: candidate test output will contain the recorded SOURCE_SHA.
Prediction 4: build and release registry aliases will resolve to the same immutable digest.
Prediction 5: cleanup will remove only dca36-release, dca36-registry and lab-owned image references/files.

5. Create the ephemeral builder and capture worker evidence

docker buildx create --name dca36-release --driver docker-container --bootstrap
docker buildx inspect dca36-release | tee dca36-checkpoint/evidence/builder.txt
docker buildx du --builder dca36-release | tee dca36-checkpoint/evidence/cache-before.txt

6. Build exactly once and capture metadata

SOURCE_SHA=$(cat dca36-checkpoint/evidence/source-sha.txt)
printf 'BUILD_INVOCATION=1
' | tee dca36-checkpoint/evidence/build-count.txt

docker buildx build   --builder dca36-release   --build-arg SOURCE_SHA="$SOURCE_SHA"   --cache-from type=local,src=dca36-checkpoint/cache   --cache-to type=local,dest=dca36-checkpoint/cache-next,mode=max   --metadata-file dca36-checkpoint/evidence/build-metadata.json   --load -t dca36-checkpoint:candidate   dca36-checkpoint/src   | tee dca36-checkpoint/evidence/build.log
rm -rf dca36-checkpoint/cache
mv dca36-checkpoint/cache-next dca36-checkpoint/cache

7. Test the exact candidate and capture image identity

SOURCE_SHA=$(cat dca36-checkpoint/evidence/source-sha.txt)
docker run --rm dca36-checkpoint:candidate | tee dca36-checkpoint/evidence/test-output.txt
grep -F "source=$SOURCE_SHA" dca36-checkpoint/evidence/test-output.txt

docker image inspect dca36-checkpoint:candidate   --format 'id={{.Id}} labels={{json .Config.Labels}}'   | tee dca36-checkpoint/evidence/candidate-inspect.txt

8. Attestation evidence plan

The loaded single-platform lab candidate is convenient for local tests. For production registry output, add --provenance=mode=min --sbom=true --push to the one build invocation and capture registry-attached evidence against the published digest. If the local registry path below supports attestations in your environment, you may repeat the checkpoint as a registry-output-only build—but that would be a separate candidate and must receive a new evidence chain. Do not pretend the loaded candidate inherited registry attestations it never had.

9. Publish the already-built candidate to the disposable registry

docker run -d --name dca36-registry --label academy.lab=dca36 -p 127.0.0.1:5000:5000 registry:2

docker tag dca36-checkpoint:candidate 127.0.0.1:5000/dca36/checkpoint:build

docker push 127.0.0.1:5000/dca36/checkpoint:build   | tee dca36-checkpoint/evidence/push-build.log

docker buildx imagetools inspect 127.0.0.1:5000/dca36/checkpoint:build   | tee dca36-checkpoint/evidence/build-ref.txt

10. Promote without rebuild

printf 'PROMOTION_BUILD_INVOCATIONS=0
' | tee dca36-checkpoint/evidence/promotion-build-count.txt

docker buildx imagetools create   --tag 127.0.0.1:5000/dca36/checkpoint:release-1   127.0.0.1:5000/dca36/checkpoint:build

docker buildx imagetools inspect 127.0.0.1:5000/dca36/checkpoint:release-1   | tee dca36-checkpoint/evidence/release-ref.txt

Compare the digest lines in build-ref.txt and release-ref.txt. A successful checkpoint has the same subject digest.

11. Downstream identity check

docker pull 127.0.0.1:5000/dca36/checkpoint:release-1   | tee dca36-checkpoint/evidence/pull-release.log

docker image inspect 127.0.0.1:5000/dca36/checkpoint:release-1 \
  --format 'id={{.Id}} repoDigests={{json .RepoDigests}} labels={{json .Config.Labels}}'   | tee dca36-checkpoint/evidence/downstream-inspect.txt

12. Cache evidence and no-second-build proof

docker buildx du --builder dca36-release | tee dca36-checkpoint/evidence/cache-after.txt
cat dca36-checkpoint/evidence/build-count.txt dca36-checkpoint/evidence/promotion-build-count.txt

The proof is procedural plus artifact-based: the checkpoint records one build invocation, promotion uses only imagetools create, and both aliases resolve to the same registry digest.

13. Exact cleanup

docker rm -f dca36-registry 2>/dev/null || true
docker image rm dca36-checkpoint:candidate   127.0.0.1:5000/dca36/checkpoint:build   127.0.0.1:5000/dca36/checkpoint:release-1 2>/dev/null || true
docker buildx rm dca36-release
printf 'Retain dca36-checkpoint/evidence for review; remove the directory only after review.
'

14. Required evidence packet

Evidence family Required record
Source full source SHA or synthetic checksum; Dockerfile checksum
Run run ID and UTC timestamps
Builder Docker/Compose/Buildx versions; builder/worker evidence
Cache before/after builder cache report; cache scope/path
Build single build log; metadata JSON; build-count statement
Test candidate output/assertion linked to source SHA
Artifact candidate image ID and registry build digest
Attestations production plan or registry-bound SBOM/provenance evidence if supported
Promotion release alias inspection; zero-build promotion statement
Downstream pulled RepoDigest/labels and expected subject match
Cleanup exact builder/registry/image resources removed; no broad prune
Limitations local registry/attestation/provider differences explicitly noted

15. What Chapter 36 adds to the production operating model

You can now design CI/CD around a stable release subject: exact source revision, isolated builder, explicitly scoped cache, one immutable digest, tests and supply-chain evidence bound to that digest, and promotion by aliasing/copying the same content. This makes rollback and incident analysis artifact-centric rather than branch-centric.

Chapter 37

Developer Workflows, Dev Containers, Inner Loop Optimization, Compose-Based Labs, and Local Parity

Chapter 37 brings these reproducibility ideas back to the developer inner loop: fast local feedback, dev containers, Compose labs, and parity without pretending local and production environments are identical.

Knowledge check

What is the strongest proof that promotion did not rebuild?

Why are test output and source SHA both in the evidence packet?

If the local registry cannot support the required attestation behavior, what should you do?

Why is the cache report not release evidence?

What is the natural rollback unit after this chapter?

Official references and version notes

Checkpoint baseline: 2026-09-22. Buildx 0.37.1, BuildKit 0.33.0, docker/build-push-action 7.4.0 and setup-buildx-action 4.3.0 are current upstream references. The runnable checkpoint remains provider-neutral and free/local/disposable.

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
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.