Slow Docker image builds in CI waste time and money — understanding and leveraging layer caching effectively can dramatically speed up your CI pipeline's build stage.
Understanding Docker Layer Caching
Each instruction in a Dockerfile creates a layer; Docker can reuse a cached layer if nothing that affects it has changed — effective caching means only genuinely changed layers need rebuilding, not the entire image from scratch each time.
Ordering Dockerfile Instructions for Better Cache Hits
# Good: dependencies installed before copying application code
COPY package.json package-lock.json ./
RUN npm install
COPY . .
# Bad: any code change invalidates the dependency install cache
COPY . .
RUN npm install
Placing rarely-changing instructions (dependency installation) before frequently-changing ones (application code copy) means dependency installation's expensive layer is cached and reused across builds where only application code changed.
Why CI Environments Often Lack Persistent Cache by Default
Many CI runners start from a clean environment each run, meaning Docker's local layer cache is empty unless you explicitly configure cache persistence — without addressing this, you lose caching benefits even with a well-ordered Dockerfile.
GitHub Actions: Using Cache Action for Docker Layers
- uses: docker/build-push-action@v5
with:
cache-from: type=gha
cache-to: type=gha,mode=max
GitHub Actions' cache backend integration for Docker builds persists layer cache between workflow runs, meaningfully speeding up subsequent builds.
Using a Registry as a Cache Source
docker build --cache-from myregistry/myapp:latest -t myapp:new .
Pulling a previous image as a cache source works across different CI runners/environments that don't share local disk state, useful when your CI infrastructure doesn't provide persistent local caching.
Multi-Stage Builds for Smaller, More Cacheable Images
See How to Set Up Multi-Stage Docker Builds for Smaller Images — separating build dependencies from your final runtime image both reduces final image size and can improve caching, since build-tool layers don't need to be part of your deployed image.
Using BuildKit for Better Caching
DOCKER_BUILDKIT=1 docker build .
BuildKit (Docker's modern build engine) offers more sophisticated caching capabilities than the legacy builder, including parallel build stages and more granular cache control.
Avoiding Cache Invalidation from Unnecessary File Changes
# .dockerignore
node_modules
.git
*.log
A proper .dockerignore file prevents irrelevant file changes (log files, editor artifacts) from unnecessarily invalidating your build context and associated cache layers.
Measuring Build Time Improvement
Track actual CI build duration over time as you apply these optimizations — confirms genuine improvement rather than assuming theoretical caching benefits translate to your specific setup without verification.
Common Errors
Cache seemingly not being used despite configuration — verify your cache backend is genuinely persisting between runs (check CI logs for cache hit/miss indicators), and confirm your Dockerfile instruction ordering genuinely supports effective caching for your specific change patterns.
Continue Reading
- How to Set Up Multi-Stage Docker Builds for Smaller Images
- How to Build a Simple CI/CD Pipeline with GitHub Actions
- How to Set Up Zero-Downtime Deployments with Docker
Browse more articles in DevOps & CI/CD.