Docker Build Cache Not Invalidating After File Change

If you change a file in your project but docker build keeps producing an image with the old content, Docker isn’t actually ignoring your change — it’s reusing a cached layer that it (incorrectly, from your point of view) still considers valid. This is one of the most common sources of “why isn’t my fix showing up” confusion when working with Docker.

The Problem

You edit a file, run docker build -t your-image ., and the container still behaves like the old version of the code. Running the container and checking the file inside it confirms the change never made it in:

docker run --rm your-image cat /app/some-file.py
# shows the OLD content, even though the file was clearly edited

Sometimes the build log even shows lines like ---> Using cache right where you’d expect your changed file to be copied in, which is the clearest signal that caching is the actual cause.

Why It Happens

Docker builds images in layers, and caches each layer based on the instruction and its inputs. The most common reasons a layer doesn’t get invalidated when you expect it to are:

  • The COPY or ADD instruction happens after a layer that installs dependencies (e.g. COPY package.json then RUN npm install then COPY . .), but the actual file you changed is outside the directory being copied at that step
  • You’re using docker compose up --build without realizing Compose sometimes reuses an existing image tag instead of rebuilding, especially if the image was built once and never explicitly removed
  • A .dockerignore file is excluding the exact file you changed, so Docker’s build context never actually contains your update
  • The file change happened on the host, but you’re building from a different context path than where you edited it (common in multi-service repos with several Dockerfiles)

The Fix

First, confirm whether the build is actually using cache at the step you expect. Run the build without the quiet flag and look for Using cache next to the relevant instruction:

docker build -t your-image . --progress=plain

If you see caching where a COPY of your changed file should be, the simplest fix is to force a clean rebuild, ignoring all cached layers:

docker build --no-cache -t your-image .

This confirms whether caching is really the issue. If the fresh build shows the correct content, the real fix is restructuring your Dockerfile so cache invalidation works correctly going forward, rather than running --no-cache every time (which makes builds much slower). A common pattern that keeps caching efficient while still invalidating correctly is copying dependency manifests first, installing dependencies, and only then copying the rest of the source code:

COPY package.json package-lock.json ./
RUN npm install
COPY . .

With this order, Docker only reuses the (slow) npm install layer when the dependency files haven’t changed, while the final COPY . . layer — and everything after it — correctly rebuilds whenever any source file changes.

If you’re using Docker Compose, also make sure you’re actually forcing a rebuild rather than reusing an old image:

docker compose build --no-cache
docker compose up -d --force-recreate

Finally, check your .dockerignore file — if the file you changed matches a pattern listed there, Docker never sees it in the first place, regardless of caching behavior:

cat .dockerignore

Still Not Working?

If you’re building on a CI/CD system or a remote builder (like a self-hosted runner or a cloud build service), it may be reusing a persistent build cache stored outside your local machine entirely. In that case, look for a cache-clearing option specific to that platform, or explicitly pass a unique --build-arg value (like a timestamp or commit hash) to one of your build steps, which forces Docker to treat that layer — and everything after it — as changed, even if the file contents technically look the same to the caching mechanism.

Leave a Comment