3.9 KiB
3.9 KiB
name, description
| name | description |
|---|---|
| docker-patterns | 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
# Good
FROM node:20.11.0-alpine3.19
# Bad — version changes can break builds
FROM node:latest
Multi-Stage Builds for minimal images
# 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
# 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
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
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
# Scan for vulnerabilities
docker scout cve myapp:latest
# Scan Dockerfile for issues
hadolint Dockerfile
Networking
Service-to-service communication
# Compose: services can reach each other by name
# app can reach db at hostname "db" on port 5432
Isolating networks
networks:
frontend: # Public-facing services
backend: # DB, cache (not exposed to outside)
Volumes
Named volumes (data persistence)
volumes:
- pgdata:/var/lib/postgresql/data # Persisted data
- redis-data:/data
Bind mounts (development)
volumes:
- ./src:/app/src:ro # Read-only code mount
- /data:/app/logs # Log output
Optimization Tips
- Smallest base image:
scratch>alpine>slim>full - Layer count: Fewer layers = smaller image
- COPY order: Dependencies first, then application code
- Combined RUN commands:
RUN apt-get update && apt-get install -y x && rm -rf /var/lib/apt/lists/* - Multi-stage: Build tools in builder, runtime only in final
- Distroless for Go/Rust:
FROM scratchwith 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