TL;DR
Master Docker multi-stage builds with this comprehensive guide covering optimization techniques, best practices, and real-world examples for creating efficient container images
Docker Multi-stage Builds: Optimizing Container Images
Multi-stage builds are a powerful feature in Docker that allows you to create efficient and secure container images by separating build-time dependencies from runtime environments. This guide explores best practices and implementation patterns for multi-stage builds.
$1
`` graph LR
subgraph "Stage 1: Build"
A[Source Code]
B[Build Tools]
C[Dependencies]
D[Compiled App]
end
subgraph "Stage 2: Runtime"
E[Base Image]
F[Runtime Deps]
G[Final App]
end
A --> B
B --> C
C --> D
D --> G
E --> F
F --> G
classDef build fill:#1a73e8,stroke:#fff,color:#fff
classDef runtime fill:#34a853,stroke:#fff,color:#fff
class A,B,C,D build
class E,F,G runtime
mermaid
`
$1
$1
` FROM node:16-alpine AS builder WORKDIR /app
COPY package*.json ./
RUN npm ci COPY . .
RUN npm run build FROM node:16-alpine WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./
RUN npm ci --only=production EXPOSE 3000
CMD ["npm", "start"]
dockerfile
`Stage 1: Build
Stage 2: Runtime
$1
` FROM golang:1.20-alpine AS builder WORKDIR /app
COPY go.* ./
RUN go mod download COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o server . FROM alpine:3.17 RUN apk add --no-cache ca-certificates
COPY --from=builder /app/server /server EXPOSE 8080
CMD ["/server"]
dockerfile
`Stage 1: Build
Stage 2: Runtime
$1
$1
` FROM node:16-alpine AS tester WORKDIR /app
COPY package*.json ./
RUN npm ci COPY . .
RUN npm run test FROM node:16-alpine AS builder WORKDIR /app
COPY package*.json ./
RUN npm ci COPY . .
RUN npm run build FROM node:16-alpine WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./
RUN npm ci --only=production EXPOSE 3000
CMD ["npm", "start"]
dockerfile
`Stage 1: Test
Stage 2: Build
Stage 3: Runtime
$1
` FROM node:16-alpine AS frontend-builder WORKDIR /app
COPY frontend/package*.json ./
RUN npm ci COPY frontend .
RUN npm run build FROM golang:1.20-alpine AS backend-builder WORKDIR /app
COPY backend/go.* ./
RUN go mod download COPY backend .
RUN CGO_ENABLED=0 GOOS=linux go build -o server . FROM alpine:3.17 RUN apk add --no-cache ca-certificates nginx COPY --from=frontend-builder /app/dist /usr/share/nginx/html COPY --from=backend-builder /app/server /server EXPOSE 80 8080
CMD ["sh", "-c", "nginx && /server"]
dockerfile
`Stage 1: Build Frontend
Stage 2: Build Backend
Stage 3: Runtime
Copy frontend assets
Copy backend binary
$1
$1
| Technique | Description | Impact |
|---|---|---|
| Cache Dependencies | Copy dependency files first | Faster builds |
| Minimize Layers | Combine RUN commands | Smaller images |
| Clean Up | Remove build artifacts | Reduced size |
$1
` FROM node:16-alpine AS builder ARG NODE_ENV=production
ARG BUILD_FLAG="" WORKDIR /app
COPY package*.json ./
RUN npm ci COPY . .
RUN npm run build ${BUILD_FLAG} FROM node:16-alpine ARG NODE_ENV=production
ENV NODE_ENV=${NODE_ENV} WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./
RUN npm ci --only=production EXPOSE 3000
CMD ["npm", "start"]
dockerfile
`Stage 1: Build
Stage 2: Runtime
$1
$1
` FROM python:3.11-slim AS builder WORKDIR /app RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH" COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt COPY . .
RUN python setup.py build FROM python:3.11-slim COPY --from=builder /opt/venv /opt/venv
COPY --from=builder /app/build /app ENV PATH="/opt/venv/bin:$PATH"
WORKDIR /app EXPOSE 8000
CMD ["gunicorn", "app:app"]
dockerfile
`Stage 1: Build
Stage 2: Runtime
$1
` FROM maven:3.9-eclipse-temurin-17 AS builder WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline COPY src ./src
RUN mvn package -DskipTests FROM eclipse-temurin:17-jre-alpine WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar EXPOSE 8080
CMD ["java", "-jar", "app.jar"]
dockerfile
`Stage 1: Build
Stage 2: Runtime
$1
$1
` name: Docker Build on:
push:
branches: [ main ]
pull_request:
branches: [ main ] jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
push: false
tags: myapp:latest
cache-from: type=gha
cache-to: type=gha,mode=max
yaml
`.github/workflows/docker-build.yml
$1
$1
| Practice | Description | Implementation |
|---|---|---|
| Minimal Base Images | Use slim/alpine variants | FROM alpine:3.17 |
| Non-root User | Run as non-privileged user | USER appuser |
| Secret Management | Use build arguments | ARG SECRET |
$1
1. Dependency Caching
` # Good practice
COPY package*.json ./
RUN npm ci
COPY . .
# Bad practice
COPY . .
RUN npm ci
dockerfile
`
2. Build Context Optimization
` # .dockerignore
node_modules
npm-debug.log
Dockerfile
.dockerignore
.git
.gitignore
README.md
dockerfile
``
$1
$1
| Issue | Cause | Solution |
|---|---|---|
| Build Failures | Missing dependencies | Check build stage |
| Large Images | Unnecessary files | Use .dockerignore |
| Cache Issues | Layer ordering | Optimize COPY |
$1
1. [Docker Multi-stage Builds](https://docs.docker.com/build/building/multi-stage/)
2. [Docker Build Performance](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/)
3. [Container Security](https://snyk.io/learn/container-security/)
4. [Docker Layer Caching](https://docs.docker.com/build/cache/)
5. [Docker BuildKit](https://docs.docker.com/build/buildkit/)
6. [Container Optimization](https://docs.docker.com/develop/develop-images/guidelines/)
$1
Why This Matters
Understanding the business and technical context helps you make informed decisions rather than blindly following patterns.
Trade-offs to Consider
Every architectural decision involves trade-offs. Consider your specific requirements, team expertise, and scale when evaluating options.
When NOT to Use This
Knowing when a solution doesn't apply is as valuable as knowing when it does. Consider alternatives for your specific situation.
Decision Framework
Use this framework to evaluate whether this approach is right for your use case based on your specific constraints and requirements.