Checkpoint Lab — Build Cache, Cache Mounts, Bind Mounts, Secret Mounts, SSH Mounts, and Cache Import/Export
The checkpoint builds a small dependency-using application on an isolated builder, measures cold/warm behavior, proves secret and SSH inputs remain ephemeral, exports a local external cache, imports it into a fresh builder, and records whether the clean builder reuses cache without treating cache as release identity.
Learning objectives
- Capture a complete cold/warm/fresh-builder cache timeline with exact Buildx, BuildKit, frontend, builder, source, and output evidence.
- Prove that a fake secret and an SSH agent are available only to the intended RUN step and do not appear in the final image configuration/history.
- Export local cache from one isolated builder, import it into another, and identify which vertices are reused without assuming the image was already loaded.
- Verify that the application output is identical across cached and uncached paths while cache directories remain disposable performance state.
- Produce a concise cache trust dossier and bridge naturally to Chapter 12 multi-platform build behavior.
1. Checkpoint scenario and success criteria
You are preparing an ephemeral CI build design. Dependency downloads are expensive, builders may be short-lived, and the release path must remain explainable when cache is absent. The checkpoint therefore treats cache as optional performance state, keeps secret and SSH material ephemeral, and proves that a fresh builder can reuse a trusted external cache without confusing that cache with release identity.
Your deliverable is an evidence packet. Success means you can explain four independent states: internal instruction/result cache, mutable package cache, ephemeral sensitive mounts, and external cache import/export. You must also prove that application behavior remains correct and that the final output identity is captured independently of cache identity.
2. Write predictions before execution
Create evidence/00-predictions.txt before building.
Include at least these predictions:
- The first build on a new builder executes dependency and application vertices.
- An unchanged second build reports instruction/result cache hits and should execute less work.
- If only application source changes, the dependency-install result should remain reusable because the requirements input did not change.
- The fake secret and SSH agent are accessible only to their intended RUN vertices and are absent from runtime state.
- A second fresh builder can reuse the exported local cache even though it starts without the original builder’s internal cache.
- The final image/OCI output identity is evidence distinct from the cache directory itself.
Afterward, mark each prediction confirmed, rejected, or qualified. Do not rewrite predictions to match the outcome.
3. Preflight and exact assumptions
mkdir -p ch11-checkpoint/{evidence,cache}
cd ch11-checkpoint
docker version | tee evidence/01-docker-version.txt
docker context show | tee evidence/02-context.txt
docker buildx version | tee evidence/03-buildx-version.txt
docker buildx ls | tee evidence/04-builders-before.txt
docker buildx create \
--name devops-academy-ch11-checkpoint \
--driver docker-container \
--use
docker buildx inspect --bootstrap \
| tee evidence/05-builder.txt
Record BuildKit version, driver, endpoint, and worker platform. If Docker or Buildx is unavailable, use the static simulation path: produce the Dockerfile, expected state transitions, and evidence schema while marking live IDs/digests/timings as not observed.
4. Create synthetic source and fake sensitive input
cat > requirements.txt <<'REQ'
requests==2.32.5
REQ
cat > app.py <<'PYAPP'
import requests
print("chapter11-checkpoint", requests.__version__)
PYAPP
printf '%s\n' 'checkpoint-fake-secret-v1' > fake-secret.txt
printf '%s\n' 'bind-input-v1' > bind-input.txt
sha256sum requirements.txt app.py bind-input.txt \
| tee evidence/06-inputs-sha256.txt
The exercise pins the Python package version for stable teaching, but a production build would also record package hashes/index identity, exact base-image digest, platform, and source revision.
5. Dockerfile with cache, bind, secret, and SSH mounts
# syntax=docker/dockerfile:1
FROM python:3.13-alpine AS build
WORKDIR /src
COPY requirements.txt .
RUN --mount=type=cache,id=ch11-checkpoint-pip,target=/root/.cache/pip,sharing=locked \
pip install --prefix=/install -r requirements.txt
RUN --mount=type=bind,source=bind-input.txt,target=/mnt/input.txt,ro \
sha256sum /mnt/input.txt > /bind-input.sha256
RUN --mount=type=secret,id=demo,required=true \
test -s /run/secrets/demo && echo secret-ok > /secret-status
RUN --mount=type=ssh,required=true \
test -n "$SSH_AUTH_SOCK" && test -S "$SSH_AUTH_SOCK" && echo ssh-ok > /ssh-status
COPY app.py .
FROM python:3.13-alpine
COPY --from=build /install /usr/local
COPY --from=build /src/app.py /app.py
COPY --from=build /bind-input.sha256 /evidence/bind-input.sha256
COPY --from=build /secret-status /evidence/secret-status
COPY --from=build /ssh-status /evidence/ssh-status
USER 65532:65532
ENTRYPOINT ["python", "/app.py"]
sha256sum Dockerfile | tee evidence/07-dockerfile-sha256.txt
Only intentional non-secret outputs cross into the runtime image. The package cache, bind source, secret file, and SSH socket do not.
6. Create a disposable SSH-agent identity
eval "$(ssh-agent -s)"
ssh-keygen -q -t ed25519 -N '' -f ./checkpoint_key
ssh-add ./checkpoint_key
ssh-add -l | tee evidence/08-ssh-agent.txt
No remote connection occurs. The agent exists only to prove that BuildKit can mount an SSH agent socket for one RUN. If OpenSSH tooling is unavailable, mark live SSH evidence not observed and complete the rest of the checkpoint.
7. Cold build and local cache export
START=$(date +%s)
docker buildx build \
--builder devops-academy-ch11-checkpoint \
--progress=plain \
--secret id=demo,src=fake-secret.txt \
--ssh default="$SSH_AUTH_SOCK" \
--cache-to type=local,dest=cache,mode=max \
--metadata-file evidence/09-cold-metadata.json \
--load \
-t devops-academy-ch11-checkpoint:app \
. 2>&1 | tee evidence/09-cold-build.txt
END=$(date +%s)
printf 'cold_seconds=%s\n' "$((END-START))" \
| tee evidence/10-cold-time.txt
Record which vertices execute and which external resources are contacted. The cache directory is an optimization artifact; the locally loaded image is a separate result.
8. Warm build with no source changes
START=$(date +%s)
docker buildx build \
--builder devops-academy-ch11-checkpoint \
--progress=plain \
--secret id=demo,src=fake-secret.txt \
--ssh default="$SSH_AUTH_SOCK" \
--load \
-t devops-academy-ch11-checkpoint:app \
. 2>&1 | tee evidence/11-warm-build.txt
END=$(date +%s)
printf 'warm_seconds=%s\n' "$((END-START))" \
| tee evidence/12-warm-time.txt
Count cached and executed vertices from plain progress. Timing is supporting evidence, not the sole proof, because host CPU/disk/network contention can change between runs.
9. Change only application source
cat > app.py <<'PYAPP'
import requests
print("chapter11-checkpoint-v2", requests.__version__)
PYAPP
sha256sum app.py | tee evidence/13-app-v2-sha256.txt
docker buildx build \
--builder devops-academy-ch11-checkpoint \
--progress=plain \
--secret id=demo,src=fake-secret.txt \
--ssh default="$SSH_AUTH_SOCK" \
--load -t devops-academy-ch11-checkpoint:app-v2 \
. 2>&1 | tee evidence/14-source-change-build.txt
The dependency-install vertex should remain reusable because
requirements.txt and earlier inputs did not change. If
it executes, inspect the log and input identities before concluding
cache malfunction.
10. Verify runtime and sensitive-input boundaries
docker run --rm devops-academy-ch11-checkpoint:app-v2 \
| tee evidence/15-runtime.txt
docker image inspect devops-academy-ch11-checkpoint:app-v2 \
--format 'ID={{.Id}} User={{.Config.User}} Env={{json .Config.Env}}' \
| tee evidence/16-image-inspect.txt
docker image history --no-trunc devops-academy-ch11-checkpoint:app-v2 \
| tee evidence/17-history.txt
docker run --rm --entrypoint /bin/sh devops-academy-ch11-checkpoint:app-v2 -c \
'test ! -e /run/secrets/demo; test -z "$SSH_AUTH_SOCK"; ls -l /evidence; cat /evidence/*' \
| tee evidence/18-sensitive-boundary.txt
Search the captured history/configuration for the literal fake
secret. The runtime should contain only secret-ok and
ssh-ok status files—not secret bytes, private keys, or
an agent socket.
11. Import cache into a fresh builder
docker buildx create \
--name devops-academy-ch11-checkpoint-fresh \
--driver docker-container
docker buildx inspect devops-academy-ch11-checkpoint-fresh --bootstrap \
| tee evidence/19-fresh-builder.txt
START=$(date +%s)
docker buildx build \
--builder devops-academy-ch11-checkpoint-fresh \
--progress=plain \
--secret id=demo,src=fake-secret.txt \
--ssh default="$SSH_AUTH_SOCK" \
--cache-from type=local,src=cache \
--metadata-file evidence/20-fresh-metadata.json \
--output type=oci,dest=app-fresh.oci.tar \
. 2>&1 | tee evidence/20-fresh-build.txt
END=$(date +%s)
printf 'fresh_seconds=%s\n' "$((END-START))" \
| tee evidence/21-fresh-time.txt
Because the new builder had no internal history, reused vertices demonstrate external cache import. Because the output is an OCI tar, do not claim it is automatically a runnable local tag.
12. Required evidence packet
| Evidence | What it proves |
|---|---|
| Docker/context/Buildx/builder capture | Exact execution context, driver, BuildKit version, worker platform. |
| Dockerfile/source hashes | Declared input identity. |
| Cold/warm/source-change logs | Which vertices executed or reused internal cache. |
| Timing files | Observed performance on this machine. |
| Local cache directory/index | External cache artifact exists separately from image output. |
| Image inspect/history/runtime | Local result identity and functional non-root runtime. |
| Sensitive-boundary checks | Secret and SSH mounts did not become runtime state. |
| Fresh-builder log | Reuse came from imported external cache. |
| Metadata JSON / OCI output | Build-result/exporter identity. |
| Prediction review | Whether each predicted state transition was observed. |
13. Prediction review
Create evidence/22-prediction-results.txt. For each
prediction, record confirmed, rejected, or qualified plus the exact
evidence file. A warm build can be slower because of unrelated host
noise while still proving cache hits; in that case qualify timing
and keep the cache conclusion tied to the progress trace.
14. Exact cleanup and rollback
docker image rm \
devops-academy-ch11-checkpoint:app \
devops-academy-ch11-checkpoint:app-v2 \
2>/dev/null || true
docker buildx rm devops-academy-ch11-checkpoint-fresh
docker buildx rm devops-academy-ch11-checkpoint
ssh-add -D 2>/dev/null || true
ssh-agent -k 2>/dev/null || true
rm -f checkpoint_key checkpoint_key.pub
docker buildx ls | tee evidence/23-builders-after.txt
Retain evidence/, cache/, and the OCI tar
only as long as needed for review. Cache artifacts still need
retention and access-control policy even when they contain no
intentional secrets.
15. Operational review and Chapter 12 handoff
Chapter 11 adds reusable performance state to the Docker evidence chain without weakening artifact identity. You can now explain whether a build was fast because a vertex was skipped, a package manager reused a cache directory, or a fresh builder imported external cache. You also proved that secret and SSH mounts are ephemeral inputs rather than image configuration.
Chapter 12 moves to Multi-Platform Images, QEMU Emulation, Native Builders, Manifest Lists, and Cross-Architecture Delivery. Cache evidence now gains a platform dimension: worker platform, build platform, target platform, emulation/native execution, and per-platform output identity must remain distinct.
Knowledge check
Why change only app.py during the checkpoint?
To prove that stable dependency inputs can remain reusable while later application vertices change.
Why create a fresh builder before cache import?
It separates external-cache reuse from the original builder’s internal cache.
What proves the fake secret did not become runtime state?
Runtime filesystem checks and image config/history checks showing no secret file/value while the intended non-secret status remains.
Does an OCI tar output imply a Docker local tag exists?
No. Exporter output and local image-store state are separate.
What new identity dimension becomes central in Chapter 12?
Build/target platform and worker identity, because cache and outputs can be platform-specific.
Official references and version notes
- Docker build cache — current cache model, optimization guidance, invalidation, and storage backends.
- Build cache invalidation — instruction matching, COPY/ADD/bind-input checksums, RUN behavior, and the rule that secret contents do not invalidate cache.
- Optimize cache usage — instruction ordering, small contexts, build bind mounts, cache mounts, and external cache patterns.
- Cache storage backends — inline, registry, local, GHA, and availability notes for other backends.
- Local cache backend — OCI-layout local cache export/import used by the mandatory checkpoint.
- Registry cache backend — separate cache artifacts, min/max modes, and driver/containerd-image-store requirements.
-
RUN --mountreference — bind, cache, tmpfs, secret, and SSH mount semantics and options. - Build secrets — passing and consuming secret/SSH mounts without baking credentials into image layers.
-
docker buildx build—--cache-from,--cache-to,--secret,--ssh, progress, metadata, and output behavior. - Build drivers — driver capabilities that affect cache backend availability and builder isolation.
- Docker Engine 29 release notes — current Engine baseline and bundled component updates.
- Buildx releases — current Buildx release history.
- BuildKit releases — current BuildKit and built-in Dockerfile frontend release history.
Version-sensitive statements were rechecked against primary
documentation on 2026-09-21. The course baseline
is Docker Engine 29.8.1, Buildx 0.37.1, BuildKit 0.33.0, and the
built-in Dockerfile frontend 1.27.0, but each executable lab
records the versions and builder driver actually present. Cache
backend support varies by driver and image-store configuration;
the mandatory path uses an isolated
docker-container builder plus the
local cache backend because it is free, inspectable,
and does not require registry credentials.
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.