patterndockerTip
Multi-stage builds reduce final image size dramatically
Viewed 0 times
multi-stageimage sizescratchbuild artifactsproduction imageCOPY --from
Problem
Production images contain build tools, compilers, dev dependencies, and intermediate artifacts, making them hundreds of MB larger than necessary and increasing attack surface.
Solution
Use multi-stage builds: compile in a full build image, then COPY only the artifacts into a minimal runtime image.
# Stage 1: build
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 ./cmd/server
# Stage 2: runtime (no Go toolchain)
FROM scratch
COPY --from=builder /app/server /server
ENTRYPOINT ["/server"]Why
Each FROM starts a new image. The final stage is what gets tagged and pushed. Earlier stages exist only as build-time intermediates. Docker never includes build-stage layers in the final image.
Gotchas
- COPY --from=builder uses the stage name or index (0-based) — always name stages with AS for readability
- Don't COPY files with root ownership if the runtime stage runs as non-root — chown at copy time: COPY --chown=appuser:appuser --from=builder ...
- Scratch images have no shell, no libc, no CA certs — copy them explicitly if needed
- You can target a specific stage with: docker build --target builder .
Code Snippets
Node.js multi-stage build separating dependency install from runtime
# Node.js multi-stage example
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
FROM node:20-alpine AS runtime
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
USER node
CMD ["node", "server.js"]Context
Building production Docker images for compiled languages or apps with heavy build toolchains
Revisions (0)
No revisions yet.