--- name: docker-patterns description: Docker and Docker Compose best practices — multi-stage builds, networking, volumes, container security, optimization. Use when writing Dockerfiles, docker-compose.yml, or containerizing applications. --- # Docker Patterns Based on ECC docker-patterns skill. ## Dockerfile Best Practices ### Pin base image versions ```dockerfile # Good FROM node:20.11.0-alpine3.19 # Bad — version changes can break builds FROM node:latest ``` ### Multi-Stage Builds for minimal images ```dockerfile # Stage 1: Build FROM golang:1.22-bookworm AS builder WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 go build -o myapp -ldflags="-s -w" # Stage 2: Runtime (small image) FROM scratch COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ COPY --from=builder /app/myapp /myapp USER 1000 CMD ["/myapp"] ``` ### Layer caching optimization ```dockerfile # Copy dependency files first, install, then copy rest COPY package.json package-lock.json ./ RUN npm ci --production=false COPY . . ``` ### .dockerignore ``` node_modules/ .git/ .env dist/ *.log __pycache__ *.pyc ``` ## Docker Compose Patterns ### Production-ready compose ```yaml name: myapp services: app: image: myapp:${APP_VERSION:-latest} restart: unless-stopped environment: DATABASE_URL: postgresql://${DB_USER}:${DB_PASSWORD}@db:5432/myapp NODE_ENV: production depends_on: db: condition: service_healthy healthcheck: test: ["CMD", "wget", "--spider", "http://localhost:3000/healthz"] interval: 30s timeout: 10s retries: 3 networks: [app-net] deploy: resources: limits: memory: 512M cpus: "0.5" db: image: postgres:16-alpine volumes: - pgdata:/var/lib/postgresql/data environment: POSTGRES_DB: myapp POSTGRES_USER: ${DB_USER} POSTGRES_PASSWORD: ${DB_PASSWORD} healthcheck: test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"] interval: 10s timeout: 5s retries: 5 networks: app-net: driver: bridge volumes: pgdata: ``` ## Container Security ### Run as non-root ```dockerfile RUN addgroup -S appgroup && adduser -S appuser -G appgroup USER appuser ``` ### No sensitive data in images - Never COPY .env files - Never hardcode credentials in Dockerfile - Use docker secrets, env vars at runtime ### Minimize attack surface - Use Alpine or distroless base images - Remove build tools from final image (multi-stage) - Only expose needed ports ### Scan images ```bash # Scan for vulnerabilities docker scout cve myapp:latest # Scan Dockerfile for issues hadolint Dockerfile ``` ## Networking ### Service-to-service communication ```yaml # Compose: services can reach each other by name # app can reach db at hostname "db" on port 5432 ``` ### Isolating networks ```yaml networks: frontend: # Public-facing services backend: # DB, cache (not exposed to outside) ``` ## Volumes ### Named volumes (data persistence) ```yaml volumes: - pgdata:/var/lib/postgresql/data # Persisted data - redis-data:/data ``` ### Bind mounts (development) ```yaml volumes: - ./src:/app/src:ro # Read-only code mount - /data:/app/logs # Log output ``` ## Optimization Tips 1. **Smallest base image**: `scratch` > `alpine` > `slim` > `full` 2. **Layer count**: Fewer layers = smaller image 3. **COPY order**: Dependencies first, then application code 4. **Combined RUN commands**: `RUN apt-get update && apt-get install -y x && rm -rf /var/lib/apt/lists/*` 5. **Multi-stage**: Build tools in builder, runtime only in final 6. **Distroless for Go/Rust**: `FROM scratch` with just the binary ## Checklist - [ ] Base image pinned to specific version - [ ] Multi-stage build for production - [ ] Non-root user - [ ] .dockerignore complete - [ ] Health checks configured - [ ] No secrets in image - [ ] Minimal base image - [ ] Volumes for persistent data - [ ] Network isolation