Multi-Stage Docker Builds

Learn multi-stage Docker builds to create smaller, more secure production images. Use build stages, copy artifacts between stages, and optimize image layers.

Images

Detailed Explanation

Multi-Stage Builds

Multi-stage builds use multiple FROM statements in a single Dockerfile. Each stage can use a different base image, and you copy only the artifacts you need into the final image.

Basic Multi-Stage Pattern

# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Stage 2: Production
FROM node:20-alpine AS production
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/server.js"]

Building Specific Stages

Use --target to build up to a specific stage:

# Build only the builder stage (e.g., for testing)
docker build --target builder -t my-app:build .

# Build the production stage
docker build --target production -t my-app:prod .

Go Application Example

Multi-stage builds are especially effective for compiled languages:

FROM golang:1.22 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o server .

FROM scratch
COPY --from=builder /app/server /server
EXPOSE 8080
ENTRYPOINT ["/server"]

The final image contains only the compiled binary — no Go toolchain, no source code. This produces images as small as 5–15 MB.

Cache Optimization

Order your COPY and RUN instructions from least to most frequently changing:

# These layers are cached if lock files haven't changed
COPY package.json package-lock.json ./
RUN npm ci

# This layer rebuilds when source code changes
COPY . .
RUN npm run build

Use Case

Building production-grade container images for Node.js, Go, Rust, and Java applications. Reducing image size from hundreds of megabytes to tens of megabytes by excluding build tools and source code from the final image.

Try It — Docker CLI Reference

Open full tool