Docker Multi-Stage Builds: Smaller Images, Faster Builds
Docker 17.05 added multi-stage builds. They’re a game-changer for image size.
The Problem
Before multi-stage builds:
FROM golang:1.8
WORKDIR /app
COPY . .
RUN go build -o myapp
CMD ["./myapp"]
Image size: 700MB (includes Go compiler, source code, build tools)
The Solution
Multi-stage builds:
# Build stage
FROM golang:1.8 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp
# Runtime stage
FROM alpine:3.6
WORKDIR /app
COPY --from=builder /app/myapp .
CMD ["./myapp"]
Image size: 15MB (only binary + Alpine)
Real Example
Our API service:
Before:
FROM node:8
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
CMD ["npm", "start"]
Image: 900MB
After:
# Build stage
FROM node:8 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# Runtime stage
FROM node:8-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./
CMD ["npm", "start"]
Image: 150MB (6x smaller!)
Benefits
- Smaller images: Only runtime dependencies
- Faster deployments: Less data to transfer
- Better security: Fewer attack vectors
- Cleaner: No build artifacts in final image
Java Example
# Build stage
FROM maven:3.5-jdk-8 AS builder
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package -DskipTests
# Runtime stage
FROM openjdk:8-jre-alpine
WORKDIR /app
COPY --from=builder /app/target/myapp.jar .
CMD ["java", "-jar", "myapp.jar"]
Before: 800MB
After: 120MB
The Verdict
Multi-stage builds are essential for production Docker images. Use them.
Questions? Let me know!