Skip to content

Build Cache

Docker build cache is a critical mechanism for reducing build times. Understanding how caching works and how to optimize it can dramatically improve your development and CI/CD workflows.

How Build Cache Works

Every instruction in a Dockerfile creates a layer. Docker caches each layer and reuses it when the inputs haven't changed.

Instruction 1: FROM node:20-alpine      ──▶ Cached ✅ (same base image)
Instruction 2: WORKDIR /app             ──▶ Cached ✅ (no change)
Instruction 3: COPY package.json ./     ──▶ Cached ✅ (file unchanged)
Instruction 4: RUN npm ci               ──▶ Cached ✅ (previous layer unchanged)
Instruction 5: COPY . .                 ──▶ MISS  ❌ (source code changed)
Instruction 6: RUN npm run build        ──▶ MISS  ❌ (previous layer changed)
Instruction 7: CMD ["node", "server"]   ──▶ MISS  ❌ (previous layer changed)

Cache Invalidation Rules

InstructionCache Invalidated When
FROMBase image digest changes
RUNCommand string changes, or previous layer invalidated
COPYFile content (checksum) changes
ADDFile content changes, URL content changes
ENVValue changes
ARGValue changes (only affects subsequent layers)
WORKDIRPath changes
EXPOSEPort specification changes
LABELLabel content changes

The Cache Chain

Cache invalidation is cascading — when one layer's cache is invalidated, all subsequent layers are also invalidated:

Layer 1: FROM node:20-alpine      ✅ Cached
Layer 2: WORKDIR /app             ✅ Cached
Layer 3: COPY package.json .      ✅ Cached
Layer 4: RUN npm ci               ✅ Cached
Layer 5: COPY . .                 ❌ INVALIDATED (source changed)
Layer 6: RUN npm run build        ❌ Rebuilt (cascade)
Layer 7: CMD ["node", "server"]   ❌ Rebuilt (cascade)

Local Build Cache

Inline Cache

The default cache is stored alongside images in the local image store:

bash
# Build with local cache (default behavior)
docker build -t my-app:1.0 .

# Second build uses cached layers
docker build -t my-app:1.0 .  # Much faster!

# Force rebuild without cache
docker build --no-cache -t my-app:1.0 .

Cache Mounts

Cache mounts persist package manager data between builds even when the RUN instruction is re-executed:

dockerfile
# syntax=docker/dockerfile:1

FROM node:20-alpine
WORKDIR /app

COPY package.json package-lock.json ./

# The npm cache directory persists between builds
RUN --mount=type=cache,target=/root/.npm \
    npm ci --only=production

COPY . .
CMD ["node", "server.js"]

Cache Mount Options

OptionDescriptionExample
targetMount path inside containertarget=/root/.npm
idCache identifierid=my-npm-cache
sharingSharing mode (shared, private, locked)sharing=locked
modeFile permissionsmode=0755
uid / gidOwner UID/GIDuid=1000,gid=1000
fromSource stage for cachefrom=builder
sourceSource path for pre-populated cachesource=/cache
dockerfile
# Multiple cache mounts with specific IDs
RUN --mount=type=cache,id=npm-cache,target=/root/.npm \
    --mount=type=cache,id=build-cache,target=/app/.next/cache \
    npm ci && npm run build

External Cache Backends

BuildKit supports exporting and importing cache from external sources, which is essential for CI/CD pipelines.

Registry Cache

Store build cache in a container registry:

bash
# Build and export cache to registry
docker buildx build \
  --cache-to type=registry,ref=myuser/my-app:buildcache,mode=max \
  --cache-from type=registry,ref=myuser/my-app:buildcache \
  -t my-app:1.0 \
  --push .

# Use the cached layers in a subsequent build
docker buildx build \
  --cache-from type=registry,ref=myuser/my-app:buildcache \
  -t my-app:1.1 \
  --push .

Inline Cache

Embed cache metadata within the image itself:

bash
# Build with inline cache metadata
docker buildx build \
  --cache-to type=inline \
  --cache-from type=registry,ref=myuser/my-app:latest \
  -t myuser/my-app:latest \
  --push .

Local Directory Cache

Store cache in a local directory (useful for CI):

bash
# Export cache to local directory
docker buildx build \
  --cache-to type=local,dest=/tmp/docker-cache \
  --cache-from type=local,src=/tmp/docker-cache \
  -t my-app:1.0 .

GitHub Actions Cache

bash
# Use GitHub Actions cache backend
docker buildx build \
  --cache-to type=gha,mode=max \
  --cache-from type=gha \
  -t my-app:1.0 .

S3 Cache

bash
# Use S3 as cache backend
docker buildx build \
  --cache-to type=s3,region=us-east-1,bucket=my-build-cache,name=my-app \
  --cache-from type=s3,region=us-east-1,bucket=my-build-cache,name=my-app \
  -t my-app:1.0 .

Cache Backend Comparison

BackendShared AcrossSetup ComplexitySpeedBest For
LocalSingle machineNoneFastestLocal development
InlineImage pullsLowFastSimple CI/CD
RegistryAll machinesLowMediumMulti-machine CI
Local DirectoryCI runnersLowFastCI/CD with persistent storage
GitHub ActionsGHA workflowsLowFastGitHub Actions CI
S3All machinesMediumMediumAWS-based CI/CD
Azure BlobAll machinesMediumMediumAzure-based CI/CD

Cache Mode

ModeDescriptionSizeUse Case
mode=minCache only final stage layersSmallerInline cache, simple images
mode=maxCache all intermediate layersLargerMulti-stage builds, CI/CD
bash
# Max mode: cache all layers (recommended for CI)
docker buildx build \
  --cache-to type=registry,ref=cache:latest,mode=max \
  --cache-from type=registry,ref=cache:latest \
  -t my-app:1.0 .

CI/CD Cache Strategies

GitHub Actions

yaml
name: Build and Push

on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

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

      - uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: myuser/my-app:latest
          cache-from: type=gha
          cache-to: type=gha,mode=max

GitLab CI

yaml
build:
  image: docker:latest
  services:
    - docker:dind
  variables:
    DOCKER_BUILDKIT: "1"
  script:
    - docker buildx create --use
    - docker buildx build
        --cache-from type=registry,ref=$CI_REGISTRY_IMAGE:cache
        --cache-to type=registry,ref=$CI_REGISTRY_IMAGE:cache,mode=max
        --push
        -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
        .

Cache Management

bash
# View build cache usage
docker buildx du

# View detailed cache info
docker buildx du --verbose

# Prune build cache
docker buildx prune

# Prune cache older than 7 days
docker buildx prune --filter "until=168h"

# Prune all cache
docker buildx prune --all

# Prune with size limit (keep 10GB)
docker buildx prune --keep-storage 10737418240

Cache Debugging

bash
# Show cache hits and misses during build
docker build --progress=plain -t my-app:1.0 . 2>&1 | grep -E "CACHED|DONE"

# Check if a specific layer is cached
docker build --progress=plain -t my-app:1.0 . 2>&1

# Example output:
# #5 [2/6] WORKDIR /app
# #5 CACHED
#
# #8 [5/6] COPY . .
# #8 DONE 0.3s   ← Not cached, was rebuilt

Cache Optimization Best Practices

PracticeImpactDescription
Order by change frequency⭐⭐⭐Least changed instructions first
Separate dependency install⭐⭐⭐Copy manifests before source code
Use cache mounts⭐⭐⭐Persist package manager caches
Use external cache in CI⭐⭐⭐Share cache across CI runs
Use mode=max for CI⭐⭐Cache all intermediate layers
Pin base image versions⭐⭐Prevent unexpected cache invalidation
Use .dockerignore⭐⭐Prevent irrelevant file changes
Avoid --no-cache in CI⭐⭐Unless cache corruption is suspected

Next Steps

基于 MIT 许可发布