How to Use Multi-Stage Build in Docker for Smaller Images
Use
FROM multiple times in a Dockerfile to create separate build stages. Copy only the needed artifacts from the build stage to the final stage using COPY --from=, which results in smaller, cleaner images.Syntax
A multi-stage Dockerfile uses multiple FROM statements to define separate stages. Each stage can have a name for easy reference. Use COPY --from=<stage-name> to copy files from one stage to another.
This helps separate the build environment from the runtime environment.
dockerfile
FROM golang:1.20 AS builder WORKDIR /app COPY . . RUN go build -o myapp FROM alpine:3.18 COPY --from=builder /app/myapp /usr/local/bin/myapp CMD ["myapp"]
Example
This example shows a Go application built in one stage and copied to a minimal Alpine image in the final stage. It demonstrates how multi-stage builds reduce image size by excluding build tools from the final image.
dockerfile
FROM golang:1.20 AS builder WORKDIR /app COPY . . RUN go build -o myapp FROM alpine:3.18 RUN apk add --no-cache ca-certificates COPY --from=builder /app/myapp /usr/local/bin/myapp CMD ["myapp"]
Common Pitfalls
- Not naming stages makes
COPY --from=harder to read and maintain. - Copying unnecessary files from build stage increases final image size.
- Forgetting to install runtime dependencies in the final stage causes runtime errors.
dockerfile
### Wrong (no stage name, copying whole directory) FROM node:18 WORKDIR /app COPY . . RUN npm install && npm run build FROM node:18-alpine COPY --from=0 /app /app CMD ["node", "/app/dist/index.js"] ### Right (named stage, copy only build output) FROM node:18 AS builder WORKDIR /app COPY package*.json ./ RUN npm install COPY . . RUN npm run build FROM node:18-alpine WORKDIR /app COPY --from=builder /app/dist ./dist COPY --from=builder /app/package*.json ./ RUN npm install --production CMD ["node", "dist/index.js"]
Quick Reference
| Command | Description |
|---|---|
| FROM | Start a new build stage with a name |
| COPY --from= | Copy files from a previous stage |
| RUN | Run commands in the current stage |
| CMD ["executable"] | Set the default command for the final image |
Key Takeaways
Use multiple FROM statements to create separate build and runtime stages.
Name your build stages for clarity and easier file copying.
Copy only necessary files from build to final stage to keep images small.
Install runtime dependencies only in the final stage to avoid bloated images.
Multi-stage builds help produce efficient, secure, and smaller Docker images.