🎉 DevOps Interview Prep Bundle is live — 1000+ Q&A across 20 topicsGet it →
All Articles

Docker Build Taking Too Long — Cache and Speed Fixes (2026)

Docker builds taking 10+ minutes every time? Here's how to fix layer caching, use BuildKit properly, and cut build times by 80% with multi-stage builds and cache mounts.

DevOpsBoysApr 30, 20263 min read
Share:Tweet

Slow Docker builds kill developer productivity and bloat CI costs. Here's every fix — from simple layer ordering to BuildKit cache mounts.


Why Docker Builds Are Slow

  1. Layer cache invalidation — wrong layer order busts the cache every time
  2. BuildKit disabled — old builder is slower and less intelligent
  3. Large build context — sending GBs to the Docker daemon
  4. No cache mounts — reinstalling all dependencies every build
  5. Single-stage builds — building everything in one fat image

Fix 1: Order Layers Correctly (Most Common Issue)

Docker caches each layer. When a layer changes, all subsequent layers rebuild. Put things that change least at the top.

Wrong order (cache busts on every code change):

dockerfile
FROM node:20-alpine
WORKDIR /app
COPY . .                    # copies everything — breaks cache on ANY file change
RUN npm install             # reinstalls every time even if package.json didn't change
RUN npm run build

Right order (dependencies cached separately):

dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./       # copy only package files first
RUN npm install             # only reinstalls when package.json changes
COPY . .                    # now copy the rest
RUN npm run build

Same principle for Python:

dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt   # cached until requirements.txt changes
COPY . .

Fix 2: Enable BuildKit

BuildKit is faster, smarter, and supports cache mounts. It's the default in Docker Desktop but may not be in CI.

bash
# Enable BuildKit
export DOCKER_BUILDKIT=1
docker build .
 
# Or in docker-compose
export COMPOSE_DOCKER_CLI_BUILD=1
export DOCKER_BUILDKIT=1
docker-compose build

In GitHub Actions:

yaml
- name: Set up Docker Buildx
  uses: docker/setup-buildx-action@v3
 
- name: Build and push
  uses: docker/build-push-action@v5
  with:
    context: .
    push: true
    tags: myapp:latest
    cache-from: type=gha          # GitHub Actions cache
    cache-to: type=gha,mode=max

Fix 3: Fix Your .dockerignore

If you're sending your entire repo to the Docker daemon, every build starts slow.

bash
# Check your build context size
docker build . 2>&1 | head -5
# "Sending build context to Docker daemon  1.2GB" <-- this is your problem

Create .dockerignore:

.git
.github
node_modules
*.log
*.md
.env*
dist/
coverage/
.DS_Store
__pycache__
*.pyc
.pytest_cache
terraform/.terraform
*.tfstate

This alone can take your build context from 1GB to 10MB.


Fix 4: BuildKit Cache Mounts (Biggest Win)

Cache mounts let you persist package manager caches between builds. npm install with cache mount goes from 2 minutes to 10 seconds.

dockerfile
# syntax=docker/dockerfile:1
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
# --mount=type=cache persists the npm cache between builds
RUN --mount=type=cache,target=/root/.npm \
    npm install
COPY . .
RUN npm run build

For Python:

dockerfile
# syntax=docker/dockerfile:1
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN --mount=type=cache,target=/root/.cache/pip \
    pip install -r requirements.txt
COPY . .

For Go:

dockerfile
# syntax=docker/dockerfile:1
FROM golang:1.22-alpine
WORKDIR /app
COPY go.mod go.sum ./
RUN --mount=type=cache,target=/go/pkg/mod \
    go mod download
COPY . .
RUN --mount=type=cache,target=/go/pkg/mod \
    --mount=type=cache,target=/root/.cache/go-build \
    go build -o /app/server .

Fix 5: Multi-Stage Builds

Single-stage builds install all dev tools and then ship them in the final image. Multi-stage builds separate build from runtime.

dockerfile
# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
 
# Stage 2: Runtime (tiny image, no dev deps)
FROM node:20-alpine AS runtime
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/index.js"]

Result: image goes from 1.2GB → 150MB. Pushes and pulls are faster too.


Fix 6: Remote Cache in CI

In CI, every build starts fresh. Use registry-based cache:

yaml
# GitHub Actions with registry cache
- name: Build and push
  uses: docker/build-push-action@v5
  with:
    context: .
    push: true
    tags: myregistry.com/myapp:${{ github.sha }}
    cache-from: type=registry,ref=myregistry.com/myapp:cache
    cache-to: type=registry,ref=myregistry.com/myapp:cache,mode=max

With ECR:

yaml
cache-from: type=registry,ref=123456789.dkr.ecr.us-east-1.amazonaws.com/myapp:cache
cache-to: type=registry,ref=123456789.dkr.ecr.us-east-1.amazonaws.com/myapp:cache,mode=max

Measuring Improvement

bash
# Time your build before and after
time docker build --no-cache .   # baseline (cold)
time docker build .               # with cache (warm)
 
# Check which layers are slow
docker build . 2>&1 | grep -E "CACHED|[0-9]+\.[0-9]+s"

Typical results after these fixes:

  • Cold build: 10 min → 4 min (multi-stage + better layers)
  • Warm build: 4 min → 20 sec (cache mounts + BuildKit cache)
  • CI build: 8 min → 45 sec (remote registry cache)
Newsletter

Stay ahead of the curve

Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.

Related Articles

Comments