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

  1. Smaller images: Only runtime dependencies
  2. Faster deployments: Less data to transfer
  3. Better security: Fewer attack vectors
  4. 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!