Docker Patterns Skill
Purpose
Best practices for containerizing applications with Docker.
When to Use
-
Creating new Dockerfiles
-
Optimizing existing images
-
Setting up local development environments
-
Preparing for production deployment
Dockerfile Patterns
Multi-Stage Build (Recommended)
Separate build and runtime environments to minimize image size.
Build stage
FROM python:3.12-slim AS builder
WORKDIR /app
Install build dependencies
RUN apt-get update && apt-get install -y --no-install-recommends
build-essential
&& rm -rf /var/lib/apt/lists/*
Install Python dependencies
COPY requirements.txt . RUN pip install --no-cache-dir --user -r requirements.txt
Production stage
FROM python:3.12-slim
WORKDIR /app
Copy only runtime dependencies
COPY --from=builder /root/.local /root/.local ENV PATH=/root/.local/bin:$PATH
Copy application code
COPY . .
Run as non-root user
RUN useradd -m -r appuser && chown -R appuser:appuser /app USER appuser
EXPOSE 8000 CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
Node.js Pattern
Build stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./ RUN npm ci --only=production
COPY . . RUN npm run build
Production stage
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/dist ./dist COPY package*.json ./
USER node
EXPOSE 3000 CMD ["node", "dist/index.js"]
Go Pattern
Build stage
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./ RUN go mod download
COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -o /app/server
Production stage - scratch for minimal size
FROM scratch
COPY --from=builder /app/server /server COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
EXPOSE 8080 ENTRYPOINT ["/server"]
Layer Optimization
Order by Change Frequency
Put rarely-changing layers first to maximize cache hits.
Least frequently changed (maximize cache)
FROM python:3.12-slim WORKDIR /app
Dependencies change occasionally
COPY requirements.txt . RUN pip install -r requirements.txt
Application code changes frequently
COPY . .
CMD ["python", "main.py"]
Combine RUN Commands
Reduce layers by combining commands.
Bad - 3 layers
RUN apt-get update RUN apt-get install -y curl RUN rm -rf /var/lib/apt/lists/*
Good - 1 layer
RUN apt-get update
&& apt-get install -y --no-install-recommends curl
&& rm -rf /var/lib/apt/lists/*
Security Best Practices
Non-Root User
Create and switch to non-root user
RUN useradd -m -r -s /bin/false appuser USER appuser
Read-Only Filesystem
docker-compose.yml
services: app: read_only: true tmpfs: - /tmp
Pin Base Image Versions
Bad - unpredictable
FROM python:latest
Good - reproducible
FROM python:3.12.1-slim-bookworm
Scan for Vulnerabilities
Using Docker Scout
docker scout cves myimage:latest
Using Trivy
trivy image myimage:latest
.dockerignore
Always include to avoid copying unnecessary files.
Git
.git .gitignore
Python
pycache *.pyc *.pyo .venv venv
Node
node_modules npm-debug.log
IDE
.vscode .idea *.swp
Docker
Dockerfile* docker-compose* .docker
Local config
.env *.local
Build artifacts
dist build *.egg-info
Tests
tests test.py test
Documentation
docs *.md !README.md
Docker Compose Patterns
Development Environment
version: '3.8'
services: app: build: context: . target: development volumes: - .:/app # Live reload - /app/node_modules # Preserve node_modules environment: - NODE_ENV=development - DEBUG=true ports: - "3000:3000" depends_on: db: condition: service_healthy
db: image: postgres:16 environment: POSTGRES_USER: dev POSTGRES_PASSWORD: devpass POSTGRES_DB: devdb volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U dev"] interval: 5s timeout: 5s retries: 5
volumes: postgres_data:
Production Environment
version: '3.8'
services: app: image: myregistry/myapp:${VERSION:-latest} deploy: replicas: 3 resources: limits: cpus: '0.5' memory: 512M reservations: cpus: '0.25' memory: 256M restart_policy: condition: on-failure max_attempts: 3 environment: - NODE_ENV=production healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/health"] interval: 30s timeout: 10s retries: 3 start_period: 40s logging: driver: json-file options: max-size: "10m" max-file: "3"
Health Checks
HTTP Health Check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3
CMD curl -f http://localhost:8000/health || exit 1
Custom Health Check Script
COPY healthcheck.sh /usr/local/bin/
HEALTHCHECK --interval=30s --timeout=10s --retries=3
CMD healthcheck.sh
Environment Variables
Build-time vs Runtime
Build-time arguments (not in final image)
ARG BUILD_VERSION=unknown
Runtime environment variables
ENV APP_VERSION=${BUILD_VERSION} ENV LOG_LEVEL=info
Using .env Files
docker-compose.yml
services: app: env_file: - .env # Base config - .env.${ENV:-local} # Environment-specific
Networking
Custom Networks
services: frontend: networks: - frontend - backend
api: networks: - backend
db: networks: - backend
networks: frontend: backend: internal: true # No external access
Volume Patterns
Named Volumes for Persistence
volumes: postgres_data: driver: local redis_data:
Bind Mounts for Development
volumes:
- ./src:/app/src:ro # Read-only
- ./config:/app/config # Read-write
Common Issues & Solutions
Issue: Large Image Size
Check what's taking space
docker history myimage:latest
Solutions:
- Use multi-stage builds
- Use slim/alpine base images
- Clean up package manager cache
- Use .dockerignore
Issue: Slow Builds
Solutions:
- Optimize layer ordering
- Use BuildKit: DOCKER_BUILDKIT=1
- Use cache mounts for package managers
RUN --mount=type=cache,target=/root/.cache/pip
pip install -r requirements.txt
Issue: Container Won't Start
Debug steps:
docker logs <container> docker exec -it <container> /bin/sh docker inspect <container>
Checklist
Before deploying:
-
Using specific base image version
-
Multi-stage build implemented
-
Running as non-root user
-
.dockerignore configured
-
Health check defined
-
Resource limits set
-
Secrets not in image
-
Image scanned for vulnerabilities
-
Logging configured
-
Graceful shutdown handled