Docker Layer Caching cache invalidation fails when non-context files change in CircleCI 2.1
0 reputation · 04 Oct 2024, 21:10 UTC
0 reputation · 04 Oct 2024, 21:10 UTC
Docker Layer Caching (DLC) was introduced in CircleCI 2.1 to store Docker image layers between jobs, reducing build times. It is enabled via the docker_layer_caching: true flag and only works with the Docker executor on plans that support caching.
The DLC cache key is derived from the Dockerfile and the build context. However, changes to files outside the Docker build context—such as a README or script not copied into the image—do not trigger cache invalidation. This can result in stale layers being reused even when the final image should differ.
When a non-context file changes, CircleCI may still serve a cached layer, leading to inconsistent build outputs. The documentation does not clarify whether this is intentional or a limitation, nor does it provide a mechanism to force invalidation based on arbitrary file changes.
33275 reputation · 04 Oct 2024, 21:35 UTC
Yes – the fact that CircleCI’s Docker Layer Caching (DLC) does not invalidate when a file outside the Docker build context changes is an intentional design decision. DLC follows the same cache‑key logic that Docker itself uses: the key is a hash of the Dockerfile and the exact tarball of the build context that is sent to the Docker daemon. Files that are not part of that context are invisible to the build process and therefore cannot affect the cache key.
Dockerfile and the build context only.config.yml option exists to extend the key with arbitrary repository files.setup_remote_docker, the remote engine receives the same context tarball, so the behavior is identical.Docker’s own layer reuse logic is based solely on the context that is sent to docker build. Because CircleCI forwards that exact context to the daemon, DLC can only react to changes that the Docker build itself sees. If a file like README.md or a CI script lives outside the context directory, Docker never touches it, so the cache key stays the same and cached layers are reused.
Use a build‑time ARG that changes when external files change.
# Dockerfile
ARG CACHE_BUST=default
COPY . .
RUN echo $CACHE_BUST
In config.yml, compute a hash of the external files and pass it as the build argument:
steps:
- run:
name: Compute external hash
command: |
EXTERNAL_HASH=$(git rev-parse HEAD)
echo "export EXTERNAL_HASH=$EXTERNAL_HASH" >> $BASH_ENV
- docker/build:
dockerfile: Dockerfile
args: {CACHE_BUST: "$EXTERNAL_HASH"}
Add a sentinel file to the context. Create a file that contains a checksum of the external files and copy it into the context before the COPY . . step. When the external files change, the sentinel changes, causing the COPY layer to rebuild.
Use save_cache / restore_cache for artifacts that aren’t part of the Docker image. This gives you fine‑grained control over when those files are cached, but it doesn’t affect the DLC cache itself.
After a job runs, look for the line Restoring Docker layer cache: … in the job log. The SHA printed there corresponds to the cache key. You can also run docker history --no-trunc $(docker images -q | tail -n1) locally to see which layers were reused.
At the time of writing, CircleCI has not added any feature to extend DLC key derivation beyond the Dockerfile and context. The current documentation reflects the intentional limitation, and no new releases have altered this logic.
If your workflow uses a remote Docker engine or a custom build context location, let us know, as that can slightly change the exact tarball that is hashed.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.