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.
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.
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.
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?
Knowledge check
Which sharing mode is a reasonable starting point for APT cache/database directories?
locked, because APT expects exclusive access to
some state.
Why not use a cache mount as the only location of a release binary?
Cache state is disposable and mutable; the build must produce its release artifact from declared inputs even when the cache is empty.
When is a build bind mount preferable to COPY?
When input is needed temporarily for one RUN and should not become a committed source layer, while still remaining a declared bounded input.
Why separate cache namespaces for untrusted PRs and releases?
To prevent untrusted jobs from becoming writers to optimization state later consumed by privileged release builds.
What does a cache hit prove about release approval?
Nothing by itself. It proves reuse of a cached result, not review, provenance, vulnerability status, signature, or runtime health.
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.