Docker
Docker Multi-stage Builds: Optimizing Container Images
Master Docker multi-stage builds with this comprehensive guide covering optimization techniques, best practices, and real-world examples for creating efficient container images
March 15, 2024
DevHub Team
5 min read
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.
Understanding Multi-stage Builds
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
Basic Multi-stage Pattern
Node.js Application Example
# Stage 1: Build FROM node:16-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # Stage 2: Runtime 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"]
Go Application Example
# Stage 1: Build 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 . # Stage 2: Runtime FROM alpine:3.17 RUN apk add --no-cache ca-certificates COPY --from=builder /app/server /server EXPOSE 8080 CMD ["/server"]
Advanced Patterns
Multi-stage with Testing
# Stage 1: Test FROM node:16-alpine AS tester WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run test # Stage 2: Build FROM node:16-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # Stage 3: Runtime 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"]
Multi-stage with Multiple Artifacts
# Stage 1: Build Frontend FROM node:16-alpine AS frontend-builder WORKDIR /app COPY frontend/package*.json ./ RUN npm ci COPY frontend . RUN npm run build # Stage 2: Build Backend 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 . # Stage 3: Runtime FROM alpine:3.17 RUN apk add --no-cache ca-certificates nginx # Copy frontend assets COPY --from=frontend-builder /app/dist /usr/share/nginx/html # Copy backend binary COPY --from=backend-builder /app/server /server EXPOSE 80 8080 CMD ["sh", "-c", "nginx && /server"]
Optimization Techniques
Layer Optimization
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 |
Build Arguments
# Stage 1: Build 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} # Stage 2: Runtime 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"]
Language-specific Patterns
Python Application
# Stage 1: Build 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 # Stage 2: Runtime 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"]
Java Spring Boot Application
# Stage 1: Build 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 # Stage 2: Runtime FROM eclipse-temurin:17-jre-alpine WORKDIR /app COPY --from=builder /app/target/*.jar app.jar EXPOSE 8080 CMD ["java", "-jar", "app.jar"]
CI/CD Integration
GitHub Actions Example
# .github/workflows/docker-build.yml 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
Best Practices
Security Considerations
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 |
Performance Tips
-
Dependency Caching
# Good practice COPY package*.json ./ RUN npm ci COPY . . # Bad practice COPY . . RUN npm ci
-
Build Context Optimization
# .dockerignore node_modules npm-debug.log Dockerfile .dockerignore .git .gitignore README.md
Troubleshooting Guide
Common Issues
Issue | Cause | Solution |
---|---|---|
Build Failures | Missing dependencies | Check build stage |
Large Images | Unnecessary files | Use .dockerignore |
Cache Issues | Layer ordering | Optimize COPY |
References
- Docker Multi-stage Builds
- Docker Build Performance
- Container Security
- Docker Layer Caching
- Docker BuildKit
- Container Optimization
Related Posts
- Docker Security Scanning - Container security
- Docker Compose V2 - Container orchestration
- Docker Desktop Alternatives - Development environment
- Docker Kubernetes Integration - Container orchestration
Docker
Containers
DevOps
Optimization