Chapter 11Lesson 03~115 minutes

Build Cache, Cache Mounts, Bind Mounts, Secret Mounts, SSH Mounts, and Cache Import/Export: Configuration, Design Choices, and Tradeoffs

Cache design is a trust and lifecycle decision. This lesson compares layer cache with cache mounts, local/registry/GHA and experimental remote backends, sharing modes, bind mounts versus COPY, and secret/SSH mounts versus ARG/ENV while connecting each choice to reproducibility, concurrency, least privilege, and CI isolation.

TradeoffsSharing modesCache backendsTrust boundariesCI design

Learning objectives

  • Choose between layer cache and cache mounts based on correctness, invalidation, mutability, and concurrency requirements.
  • Select cache sharing modes and external backends according to writer behavior, driver capabilities, trust domain, and CI persistence.
  • Choose bind mounts versus COPY based on whether files should become image/cache inputs or only temporary build inputs.
  • Use secret and SSH mounts instead of ARG/ENV or copied credentials, while understanding what metadata still affects the cache key.
  • Design cache boundaries for trusted branches, untrusted pull requests, local development, and ephemeral CI builders.
Chapter 11 evidence baseline — verified 2026-09-21. Mandatory exercises use only synthetic source/data, a fake token, an ephemeral local SSH identity, isolated chapter-owned Buildx builders, a local cache directory, and public base images. At verification time Docker Engine 29.8.1, Buildx 0.37.1, and BuildKit 0.33.0 with built-in Dockerfile frontend 1.27.0 are the current course baselines, but every lab records the actual installed versions, builder driver, worker platform, and frontend behavior. Docker currently documents bind/cache/secret/SSH RUN mounts; cache sharing modes shared/private/locked; secret values excluded from cache checksum; and local/inline/registry/GHA cache backends with driver-dependent support. Cache is never treated as release identity, and no real credentials, production registry, production daemon, or broad prune operation is required.

1. Cache architecture starts with correctness and trust

The first design question is not “which cache backend is fastest?” It is “what is allowed to influence this build, and can the build still produce the correct result from an empty cache?” Once correctness is independent of cache, optimization choices can be made by measuring cost, concurrency, persistence, and trust boundaries.

For every cache mechanism, write down four things: owner, scope, lifecycle, and invalidation. A cache with no owner or scope tends to become shared infrastructure by accident. A cache with no lifecycle grows until someone deletes it during an incident. A cache with no invalidation model causes stale-result arguments. A cache with no trust model can allow untrusted code to influence a later privileged build.

2. Layer/result cache versus cache mount

Dimension Instruction/result cache Cache mount
What is reused? An operation result can be skipped entirely. A mutable directory is available while the operation executes.
Correctness rule Reuse is valid only when cache key matches declared inputs. Build must succeed with empty or arbitrary valid cache contents.
Typical use Unchanged COPY/RUN/build stages. Package downloads, compiler object cache, language module cache.
Concurrency Managed by BuildKit result graph. Choose shared/private/locked according to the tool.
Security concern Wrong trust-domain import can reuse attacker-influenced results. Mutable files can be poisoned or leak metadata across jobs if shared broadly.

A common anti-pattern is to use a cache mount as undeclared persistent storage for generated source or release artifacts. That makes the build non-hermetic: a clean builder produces a different result or fails. Persist package-manager downloads, not the only copy of the artifact you intend to release.

3. Cache-mount sharing modes are data-consistency decisions

sharing=shared maximizes concurrency but assumes the tool can tolerate simultaneous writers. sharing=locked serializes writers and is appropriate when the cache contains a database-like structure that needs exclusive access, as Docker’s APT example demonstrates. sharing=private avoids writer interference by giving concurrent operations separate cache instances, trading storage for isolation.

The right choice depends on the package manager/compiler, not on a generic Docker preference. Measure contention. If builds queue for a locked cache longer than a cold download would take, a private or namespaced cache may be better.

4. Bind mount versus COPY: temporary input or image/cache input?

Use COPY when the file belongs in the build-stage filesystem and should participate naturally in subsequent layer history/cache. Use a build bind mount when a large input is needed only for one operation and should not become a committed source layer. Both inputs still need a bounded, reviewable identity. A remote builder resolves the build context through BuildKit; it does not magically read arbitrary client filesystem paths outside supplied contexts.

When the mounted source is writable, writes are discarded after the RUN. That can be useful for tools that insist on modifying their source directory, but any intended output must be copied or written outside the mount target before the instruction ends.

5. Secret/SSH mounts versus ARG, ENV, and copied credentials

ARG and ENV are configuration mechanisms, not secret transports. Docker documents that build arguments can be visible in history/provenance and environment values can persist in image configuration. Copying a credential into the context/layer is worse because deleting it in a later layer does not erase the earlier blob. Build secret and SSH mounts are scoped alternatives.

Need Prefer Avoid
API token for one RUN --secret + type=secret ARG TOKEN or ENV TOKEN
Private Git over SSH --ssh + type=ssh + host verification Copying private key into context/image
Non-secret build mode/version ARG Secret mount when value is not sensitive
Runtime non-secret default ENV when appropriate Build secret, which does not become runtime config

A secret mount can be marked required=true. Prefer failure over silently building a reduced or unauthenticated variant when the secret is mandatory.

6. External cache backend tradeoffs

Backend Strength Constraint / trust note
local Simple, inspectable OCI-layout directory; excellent for learning and self-managed transfer. You own retention, access control, transport, and disk growth.
inline Cache metadata travels with image output. Tied to image exporter; less flexible for rich multi-stage cache than separate registry cache.
registry Separate cache reference, supports richer modes and CI sharing. Needs registry auth/policy and appropriate driver/image-store support.
gha Convenient in GitHub Actions with branch/cache access controls. Platform limits and workflow trust/event permissions matter.
S3 / Azure Blob Object-storage integration for specialized infrastructure. Current Docker documentation marks these backends experimental.

Never place secret material deliberately into cache just because the cache is “private.” Dedicated secret mounts exist precisely to avoid persistent credential state.

7. Cache trust domains in CI/CD

Imagine a public repository where pull requests from forks can run builds. A release job later has registry credentials and signing keys. If both jobs write to the same broad cache namespace, untrusted code may be able to seed artifacts that a privileged build later considers reusable. BuildKit’s content addressing and cache-key model reduce accidental mismatch, but they do not eliminate policy questions about what untrusted builds are allowed to export or which caches trusted jobs may import.

A conservative design separates write namespaces by trust level: fork/PR jobs may read a safe base cache but write only to an isolated scope; trusted main/release jobs import from reviewed/trusted cache locations and publish release cache under controlled credentials. Cache optimization should never become a privilege-escalation channel.

8. Reproducibility: cache hit and identical output are separate claims

Two builds can produce the same image digest with different cache histories, or different digests even though both had many cache hits. Cache tells you about reuse; artifact digest tells you about output identity. For reproducibility investigations, compare source revision, base digests, dependency locks, build platform, frontend/builder versions, non-secret build args, timestamps where relevant, and output digest. Then treat cache hit rate as a performance metric layered on top.

9. Worked scenario: three environments

Developer laptop: use a local isolated builder and cache mounts for language/package downloads. Internal cache can be long-lived, but exact project-scoped IDs avoid unrelated collisions. Ephemeral CI: internal cache disappears with the runner, so import/export a trusted external cache; keep secrets in mounts and scope cache writes by branch/trust. Release pipeline: prioritize immutable base/source/dependency identity, then import only cache sources permitted by release policy. Build once, verify the resulting digest, and do not confuse “cache restored successfully” with “release is approved.”

10. Decision checklist

  • Can the build succeed from empty cache?
  • Which step is actually expensive, and is the bottleneck download, compilation, context transfer, or something else?
  • Does the tool tolerate concurrent writers? If not, choose locked/private or separate IDs.
  • Does the input belong in the image/cache graph or only temporarily in one RUN?
  • Is any value sensitive? If yes, do not route it through ARG/ENV/COPY.
  • Who may write this cache, and who may import it later?
  • Which builder driver and image-store configuration supports the desired external backend?
  • What exact evidence will prove the optimization helped without changing output correctness?
Next lesson

Next: Diagnostics, Failure Modes, Security, and Performance

Apply the decision model to stale, poisoned, concurrent, and secret-sensitive cache failures without destructive shortcuts.

Knowledge check

Which sharing mode is a reasonable starting point for APT cache/database directories?

Why not use a cache mount as the only location of a release binary?

When is a build bind mount preferable to COPY?

Why separate cache namespaces for untrusted PRs and releases?

What does a cache hit prove about release approval?

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 --mount reference — 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 and compatibility note

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.

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