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.
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
- Layer cache invalidation — wrong layer order busts the cache every time
- BuildKit disabled — old builder is slower and less intelligent
- Large build context — sending GBs to the Docker daemon
- No cache mounts — reinstalling all dependencies every build
- 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):
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 buildRight order (dependencies cached separately):
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 buildSame principle for Python:
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.
# Enable BuildKit
export DOCKER_BUILDKIT=1
docker build .
# Or in docker-compose
export COMPOSE_DOCKER_CLI_BUILD=1
export DOCKER_BUILDKIT=1
docker-compose buildIn GitHub Actions:
- 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=maxFix 3: Fix Your .dockerignore
If you're sending your entire repo to the Docker daemon, every build starts slow.
# Check your build context size
docker build . 2>&1 | head -5
# "Sending build context to Docker daemon 1.2GB" <-- this is your problemCreate .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.
# 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 buildFor Python:
# 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:
# 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.
# 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:
# 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=maxWith ECR:
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=maxMeasuring Improvement
# 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)
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
GitHub Actions Docker Push: Permission Denied / Unauthorized Fix (2026)
Getting 'permission denied' or 'unauthorized: authentication required' when pushing Docker images in GitHub Actions? Here are all the causes and fixes.
AWS ECR Image Push Access Denied — Every Fix (2026)
docker push to ECR fails with 'Access Denied' or 'no basic auth credentials'. Here's every cause — expired token, wrong region, missing IAM permissions, ECR URI mismatch — and the exact fix for each.
AWS ECS Task Keeps Stopping — How to Fix It (2026)
Your ECS task starts and then immediately stops or keeps restarting. Here's every reason this happens and how to debug and fix it.