Claude-skill-registry Container Best Practices
Expert guidance on dockerfile optimization, multi-stage builds, layer caching strategies, and base image selection. Activates when working with "dockerfile", "docker-compose", "multi-stage", "container optimization", "image layers", or "build cache".
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/container-best-practices" ~/.claude/skills/majiayu000-claude-skill-registry-container-best-practices && rm -rf "$T"
skills/data/container-best-practices/SKILL.mdContainer Best Practices Skill
Overview
Apply industry-standard best practices for building optimized, maintainable, and efficient Docker containers. Master multi-stage builds, layer caching, base image selection, and build optimization strategies to create production-ready container images.
Core Principles
Write Efficient Dockerfiles
Order Instructions for Maximum Cache Efficiency:
Structure Dockerfile layers from least to most frequently changing:
# 1. Base image (changes rarely) FROM node:20-alpine AS base # 2. System dependencies (changes occasionally) RUN apk add --no-cache \ python3 \ make \ g++ # 3. Working directory setup WORKDIR /app # 4. Package manifest files (changes moderately) COPY package.json package-lock.json ./ # 5. Dependencies installation (leverages cache when manifest unchanged) RUN npm ci --only=production # 6. Application code (changes frequently) COPY . . # 7. Build step (only when code changes) RUN npm run build # 8. Runtime configuration EXPOSE 3000 CMD ["node", "dist/main.js"]
Combine RUN Commands to Reduce Layers:
Minimize image size by chaining related commands:
# Bad: Creates 3 layers RUN apt-get update RUN apt-get install -y curl RUN apt-get clean # Good: Creates 1 layer RUN apt-get update && \ apt-get install -y curl && \ apt-get clean && \ rm -rf /var/lib/apt/lists/*
Use .dockerignore to Exclude Unnecessary Files:
Create
.dockerignore to prevent copying build artifacts and sensitive files:
# Development files node_modules/ npm-debug.log* .git/ .gitignore # Test files *.test.js coverage/ .nyc_output/ # Documentation README.md docs/ # IDE files .vscode/ .idea/ # Environment files .env .env.local *.key *.pem
Implement Multi-Stage Builds
Separate Build and Runtime Environments:
Use multi-stage builds to create minimal production images:
# Stage 1: Build environment with all dev dependencies FROM node:20 AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build && \ npm run test # Stage 2: Production environment with only runtime dependencies FROM node:20-alpine AS production WORKDIR /app # Copy only necessary files from builder COPY --from=builder /app/dist ./dist COPY --from=builder /app/node_modules ./node_modules COPY package*.json ./ # Security: Run as non-root user RUN addgroup -g 1001 -S nodejs && \ adduser -S nodejs -u 1001 && \ chown -R nodejs:nodejs /app USER nodejs EXPOSE 3000 CMD ["node", "dist/main.js"]
Create Specialized Build Targets:
Define multiple targets for different use cases:
# Development stage with hot reload FROM node:20-alpine AS development WORKDIR /app COPY package*.json ./ RUN npm install COPY . . EXPOSE 3000 9229 CMD ["npm", "run", "dev"] # Testing stage with test dependencies FROM development AS testing RUN npm run lint && \ npm run test:unit && \ npm run test:integration # Build stage FROM node:20 AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # Production stage (minimal) FROM node:20-alpine AS production WORKDIR /app COPY --from=builder /app/dist ./dist COPY --from=builder /app/node_modules ./node_modules USER node EXPOSE 3000 CMD ["node", "dist/main.js"]
Build specific stages:
# Development docker build --target development -t myapp:dev . # Testing docker build --target testing -t myapp:test . # Production docker build --target production -t myapp:prod .
Optimize Base Image Selection
Choose Minimal Base Images:
Select the smallest viable base image for your runtime:
# Python examples by size FROM python:3.11-alpine # ~50MB (best for simple apps) FROM python:3.11-slim # ~120MB (good compatibility) FROM python:3.11 # ~900MB (avoid in production) # Node.js examples by size FROM node:20-alpine # ~110MB (best for production) FROM node:20-slim # ~170MB (good compatibility) FROM node:20 # ~900MB (avoid in production) # Distroless images (Google) FROM gcr.io/distroless/nodejs20-debian12 # Minimal attack surface FROM gcr.io/distroless/python3-debian12 # No shell, no package manager
Use Distroless for Maximum Security:
Distroless images contain only your application and runtime dependencies:
# Build stage FROM node:20 AS builder WORKDIR /app COPY package*.json ./ RUN npm ci --only=production COPY . . # Production: Distroless image FROM gcr.io/distroless/nodejs20-debian12 WORKDIR /app COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/dist ./dist USER nonroot:nonroot EXPOSE 3000 CMD ["dist/main.js"]
Pin Specific Versions:
Always use specific version tags for reproducibility:
# Bad: Version changes unexpectedly FROM node:20 # Good: Specific version pinned FROM node:20.11.1-alpine3.19 # Better: Use digest for immutability FROM node:20.11.1-alpine3.19@sha256:abc123...
Leverage Build Cache Effectively
Structure for Cache Reuse:
Place frequently changing instructions after stable ones:
FROM python:3.11-slim # System packages (rarely change) RUN apt-get update && \ apt-get install -y --no-install-recommends \ postgresql-client && \ apt-get clean # Requirements (change occasionally) COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # Application code (changes frequently) COPY . . CMD ["python", "app.py"]
Use BuildKit Cache Mounts:
Enable BuildKit for advanced caching:
# syntax=docker/dockerfile:1.4 FROM python:3.11-slim # Cache pip downloads across builds RUN --mount=type=cache,target=/root/.cache/pip \ pip install -r requirements.txt # Cache apt packages RUN --mount=type=cache,target=/var/cache/apt \ --mount=type=cache,target=/var/lib/apt \ apt-get update && \ apt-get install -y postgresql-client
Build with BuildKit:
DOCKER_BUILDKIT=1 docker build -t myapp:latest .
Implement Layer Caching in CI/CD:
Use registry cache for faster CI builds:
# GitHub Actions with BuildKit cache docker buildx build \ --cache-from type=registry,ref=ghcr.io/org/app:cache \ --cache-to type=registry,ref=ghcr.io/org/app:cache,mode=max \ --tag ghcr.io/org/app:latest \ --push .
Optimize Runtime Configuration
Configure Health Checks:
Define health checks for container orchestration:
FROM node:20-alpine WORKDIR /app COPY . . # HTTP health check HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD node healthcheck.js # Alternative: Using curl HEALTHCHECK --interval=30s --timeout=3s \ CMD curl -f http://localhost:3000/health || exit 1 EXPOSE 3000 CMD ["node", "server.js"]
Set Appropriate Working Directory:
Always use absolute paths for WORKDIR:
# Bad: Relative path WORKDIR app # Good: Absolute path WORKDIR /app # Better: Clear organization WORKDIR /opt/application
Use COPY Instead of ADD:
Prefer COPY for clarity unless you need ADD's special features:
# Bad: ADD has implicit behavior (extracts tars, fetches URLs) ADD archive.tar.gz /app/ # Good: COPY is explicit and predictable COPY src/ /app/src/ COPY package.json /app/ # OK: ADD when you need tar extraction ADD rootfs.tar.gz /
Docker Compose Best Practices
Structure Compose Files for Environments
Create Environment-Specific Overrides:
Use base compose file with environment overrides:
# docker-compose.yml (base) version: '3.9' services: app: build: context: . dockerfile: Dockerfile environment: NODE_ENV: production restart: unless-stopped db: image: postgres:16-alpine volumes: - postgres_data:/var/lib/postgresql/data environment: POSTGRES_DB: ${DB_NAME} POSTGRES_USER: ${DB_USER} POSTGRES_PASSWORD: ${DB_PASSWORD} volumes: postgres_data:
# docker-compose.override.yml (development) version: '3.9' services: app: build: target: development volumes: - .:/app - /app/node_modules ports: - "3000:3000" - "9229:9229" # Debug port environment: NODE_ENV: development db: ports: - "5432:5432"
# docker-compose.prod.yml (production) version: '3.9' services: app: build: target: production deploy: replicas: 3 resources: limits: cpus: '0.5' memory: 512M logging: driver: "json-file" options: max-size: "10m" max-file: "3"
Use with:
# Development (uses docker-compose.override.yml automatically) docker compose up # Production docker compose -f docker-compose.yml -f docker-compose.prod.yml up
Implement Dependency Management
Control Startup Order:
Use depends_on with health checks:
version: '3.9' services: app: image: myapp:latest depends_on: db: condition: service_healthy redis: condition: service_started db: image: postgres:16-alpine healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 10s timeout: 5s retries: 5 redis: image: redis:7-alpine healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 5s timeout: 3s retries: 5
Configure Resource Limits
Set Memory and CPU Constraints:
Prevent resource exhaustion:
version: '3.9' services: app: image: myapp:latest deploy: resources: limits: cpus: '1.0' memory: 1G reservations: cpus: '0.5' memory: 512M mem_limit: 1g cpus: 1.0
Build Optimization Strategies
Implement Build Arguments
Parameterize Builds:
Use ARG for build-time configuration:
ARG NODE_VERSION=20 FROM node:${NODE_VERSION}-alpine ARG BUILD_DATE ARG VERSION ARG REVISION LABEL org.opencontainers.image.created="${BUILD_DATE}" \ org.opencontainers.image.version="${VERSION}" \ org.opencontainers.image.revision="${REVISION}" ARG NODE_ENV=production ENV NODE_ENV=${NODE_ENV} WORKDIR /app COPY . . RUN npm ci --only=${NODE_ENV} CMD ["node", "server.js"]
Build with arguments:
docker build \ --build-arg NODE_VERSION=20 \ --build-arg VERSION=1.2.3 \ --build-arg BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \ --build-arg REVISION=$(git rev-parse --short HEAD) \ -t myapp:1.2.3 .
Use Multi-Platform Builds
Build for Multiple Architectures:
Create images for AMD64 and ARM64:
# Create buildx builder docker buildx create --name multiarch --use # Build for multiple platforms docker buildx build \ --platform linux/amd64,linux/arm64 \ --tag ghcr.io/org/app:latest \ --push .
Platform-specific optimizations:
FROM --platform=$BUILDPLATFORM node:20-alpine AS builder ARG TARGETPLATFORM ARG BUILDPLATFORM RUN echo "Building on $BUILDPLATFORM for $TARGETPLATFORM" WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build FROM node:20-alpine COPY --from=builder /app/dist ./dist CMD ["node", "dist/main.js"]
Performance Optimization
Minimize Image Size
Remove Unnecessary Files:
Clean up in the same layer:
FROM ubuntu:22.04 RUN apt-get update && \ apt-get install -y --no-install-recommends \ build-essential \ python3 && \ # Build your app here apt-get purge -y build-essential && \ apt-get autoremove -y && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
Analyze Image Layers:
Use dive to inspect layers:
# Install dive brew install dive # Analyze image dive myapp:latest
Implement Parallel Builds
Speed Up Multi-Stage Builds:
Use multiple FROM statements for parallel builds:
# These stages build in parallel FROM node:20 AS deps WORKDIR /app COPY package*.json ./ RUN npm ci FROM node:20 AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build FROM node:20 AS tester WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . RUN npm test # Final stage depends on builder and tester FROM node:20-alpine WORKDIR /app COPY --from=builder /app/dist ./dist COPY --from=deps /app/node_modules ./node_modules CMD ["node", "dist/main.js"]
Official References
- Dockerfile Best Practices: https://docs.docker.com/develop/dev-best-practices/
- Multi-Stage Builds: https://docs.docker.com/build/building/multi-stage/
- BuildKit: https://docs.docker.com/build/buildkit/
- Docker Compose Reference: https://docs.docker.com/compose/compose-file/
- Build Cache: https://docs.docker.com/build/cache/
Related Skills
- Container Security - Hardening and vulnerability management
- Docker Skill - Container runtime operations
- DevOps Practices - CI/CD integration and deployment