Introduction

Docker layer caching in GitHub Actions often fails silently in the sense that the workflow “has caching configured” but every build still behaves like a cold build. The root problem is usually one of three things: the workflow is not using a cross-run cache backend correctly, the Dockerfile invalidates the heavy layers too early, or teams are caching the wrong thing entirely.

Symptoms

  • Docker builds redownload and rebuild almost everything on each run
  • Build output shows very few or no useful cache hits
  • Build times remain high despite enabling Buildx or cache settings
  • Minor source changes invalidate expensive dependency or package-install layers

Common Causes

  • The workflow does not use a persistent cache backend such as GitHub Actions cache for Buildx
  • Dockerfile layers are ordered so frequently changing files bust expensive cache layers
  • Package-manager downloads and build artifacts are not separated clearly
  • Teams assume local Docker cache behavior will match CI automatically

Step-by-Step Fix

  1. 1.Use Buildx with explicit cache import and export
  2. 2.Cross-run Docker layer reuse in GitHub Actions needs a deliberate cache backend strategy.

```yaml - uses: docker/setup-buildx-action@v3

  • uses: docker/build-push-action@v5
  • with:
  • context: .
  • push: false
  • cache-from: type=gha
  • cache-to: type=gha,mode=max
  • `
  1. 1.Reorder the Dockerfile for cache stability
  2. 2.Put dependency installation before frequently changing application source where possible.
dockerfile
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
  1. 1.Use BuildKit cache mounts for package managers where helpful
  2. 2.This can reduce repeated download cost even when image layers change more often.
  3. 3.Check whether cache hits are actually happening
  4. 4.Do not trust the existence of cache settings alone. Review the build output and total runtime difference.

Prevention

  • Standardize one Docker layer caching pattern across repositories
  • Keep Dockerfile layers ordered from least to most volatile
  • Measure build duration before and after caching changes
  • Treat cache configuration as part of Dockerfile design, not an afterthought