Awesome-omni-skill docker-best-practices
Create production-grade Dockerfiles optimized for speed, security, and minimal size. Use when creating or reviewing Dockerfiles, docker-compose files, or when optimizing container images for Python, Node.js, or multi-runtime environments.
install
source · Clone the upstream repo
git clone https://github.com/diegosouzapw/awesome-omni-skill
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/devops/docker-best-practices" ~/.claude/skills/diegosouzapw-awesome-omni-skill-docker-best-practices && rm -rf "$T"
manifest:
skills/devops/docker-best-practices/SKILL.mdsource content
Docker Best Practices
Production-grade Docker patterns optimized for fast builds, minimal size, and bulletproof security. Every container spins up in seconds, syncs efficiently, and never accumulates bloat.
Core Principles
- Multi-stage builds — Separate build dependencies from runtime
- Pinned versions — Never use
tags:latest - Layer caching — Order instructions from least to most frequently changing
- Minimal attack surface — Distroless or slim base images, non-root users
- BuildKit features — Cache mounts, secrets, multi-platform builds
- Fast startup — Eager dependencies, lazy application code
Quick Reference
Python API Services (FastAPI, Flask)
# syntax=docker/dockerfile:1.7 FROM python:3.11.11-slim AS base WORKDIR /app # Install system dependencies (cached unless changed) RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates \ && rm -rf /var/lib/apt/lists/* # Create non-root user RUN groupadd -g 1000 app && \ useradd -m -u 1000 -g app app # ─── Build stage ─── FROM base AS builder RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential \ && rm -rf /var/lib/apt/lists/* # Install dependencies with cache mount COPY pyproject.toml ./ RUN --mount=type=cache,target=/root/.cache/pip \ pip install --no-cache-dir . # ─── Production stage ─── FROM base AS production COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages COPY --from=builder /usr/local/bin /usr/local/bin COPY . . USER app EXPOSE 8000 HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ CMD python -c "import httpx; httpx.get('http://localhost:8000/health', timeout=2)" CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
Multi-Runtime Sandbox (Python + Node + Tools)
# syntax=docker/dockerfile:1.7 FROM ubuntu:22.04.5 AS base ENV DEBIAN_FRONTEND=noninteractive \ LANG=C.UTF-8 \ LC_ALL=C.UTF-8 # Single apt layer for all system packages RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,target=/var/lib/apt,sharing=locked \ apt-get update && apt-get install -y --no-install-recommends \ # Core utilities bash curl wget git jq unzip ca-certificates \ # Build tools (for native extensions) build-essential libffi-dev libssl-dev \ # FUSE for S3 mount fuse libfuse2 \ # Process management tini \ # Python python3.11 python3.11-venv python3.11-dev python3-pip \ && ln -sf /usr/bin/python3.11 /usr/bin/python3 \ && ln -sf /usr/bin/python3.11 /usr/bin/python \ && rm -rf /var/lib/apt/lists/* # Python package installs with cache mount RUN --mount=type=cache,target=/root/.cache/pip \ python3 -m pip install --no-cache-dir --upgrade pip setuptools wheel # Node.js via official NodeSource script RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \ apt-get install -y --no-install-recommends nodejs && \ rm -rf /var/lib/apt/lists/* && \ npm install -g npm@latest # Create non-root user RUN groupadd -g 1000 agent && \ useradd -m -u 1000 -g agent -s /bin/bash agent USER agent WORKDIR /home/agent ENTRYPOINT ["tini", "--"] CMD ["/bin/bash"]
Development vs Production Patterns
# ─── Development stage (hot reload, debug tools) ─── FROM base AS development RUN --mount=type=cache,target=/root/.cache/pip \ pip install --no-cache-dir -e ".[dev]" COPY . . # Mount source as volume for hot reload CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--reload"] # ─── Production stage (minimal, optimized) ─── FROM base AS production COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages COPY . . USER app CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0"]
Essential Patterns
Version Pinning
# ❌ NEVER use latest FROM python:3.11-slim # ✅ ALWAYS pin to patch version FROM python:3.11.11-slim # ✅ Pin all base images with digest for immutability FROM python:3.11.11-slim@sha256:abc123...
Check latest versions:
- Python: https://hub.docker.com/_/python/tags
- Node: https://hub.docker.com/_/node/tags
- Ubuntu: https://hub.docker.com/_/ubuntu/tags
BuildKit Cache Mounts
# ❌ Old way — downloads every build RUN pip install -r requirements.txt # ✅ New way — reuses download cache RUN --mount=type=cache,target=/root/.cache/pip \ pip install -r requirements.txt # ✅ npm cache mount RUN --mount=type=cache,target=/root/.npm \ npm ci --prefer-offline # ✅ apt cache mount (shared across concurrent builds) RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,target=/var/lib/apt,sharing=locked \ apt-get update && apt-get install -y package-name
Layer Ordering for Optimal Caching
# Install dependencies (rarely change) — cached ✅ COPY pyproject.toml ./ RUN pip install . # Copy application code (changes often) — rebuilt on every change ✅ COPY . .
Health Checks
# Python API HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ CMD python -c "import httpx; httpx.get('http://localhost:8000/health')" # Node.js API HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ CMD node -e "require('http').get('http://localhost:3000/health', (r) => process.exit(r.statusCode === 200 ? 0 : 1))" # General process check HEALTHCHECK --interval=30s --timeout=5s CMD pgrep -x python3 || exit 1
Security Hardening
# 1. Non-root user RUN groupadd -g 1000 app && \ useradd -m -u 1000 -g app app USER app # 2. Read-only root filesystem (in docker-compose or k8s) # docker run --read-only --tmpfs /tmp ... # 3. Drop capabilities (in docker-compose) # security_opt: # - no-new-privileges:true # cap_drop: # - ALL # 4. Minimal base image (distroless for Python/Node) FROM gcr.io/distroless/python3-debian12:latest
docker-compose.yml Best Practices
services: api: build: context: . dockerfile: Dockerfile target: production # Multi-stage target cache_from: - type=registry,ref=ghcr.io/org/api:buildcache image: org/api:latest restart: unless-stopped # Security security_opt: - no-new-privileges:true cap_drop: - ALL read_only: true tmpfs: - /tmp # Resources deploy: resources: limits: cpus: '2' memory: 2G reservations: cpus: '1' memory: 1G # Health check healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health"] interval: 30s timeout: 5s retries: 3 start_period: 10s # Env env_file: .env environment: - NODE_ENV=production
.dockerignore
CRITICAL: Always create
.dockerignore to exclude unnecessary files from build context.
# Version control .git/ .github/ .gitignore # Python __pycache__/ *.pyc *.pyo *.pyd .Python *.so *.egg-info/ dist/ build/ .venv/ venv/ *.pytest_cache/ .coverage # Node node_modules/ npm-debug.log* .npm/ # IDE .vscode/ .idea/ *.swp *.swo .DS_Store # Env files (never copy .env to image) .env .env.* !.env.example # CI/CD .github/ *.md !README.md # Docs docs/ *.md
Dockerfile Review Checklist
When creating or reviewing a Dockerfile, verify:
Performance
- Multi-stage build for services with build dependencies
-
for pip/npm/apt downloads--mount=type=cache - Layer ordering: system packages → language runtime → dependencies → app code
-
excludes unnecessary files.dockerignore - Single
per stage (not multiple RUN commands)apt-get update && install && rm -rf
Security
- Base image pinned to specific version (no
):latest - Non-root user for runtime
- Minimal base image (slim, alpine, or distroless)
- No secrets in ENV or build args (use
instead)--mount=type=secret - Health check defined
Size
-
on apt-get--no-install-recommends -
after apt-getrm -rf /var/lib/apt/lists/* -
on pip installs--no-cache-dir - Multi-stage excludes build tools from final image
- Production stage only includes runtime dependencies
Correctness
-
set before COPYWORKDIR - Correct user ownership for copied files if using non-root user
-
matches actual portEXPOSE -
/CMD
is production-appropriate (noENTRYPOINT
)--reload - Environment variables have sensible defaults
Common Mistakes to Avoid
❌ Installing dev dependencies in production
FROM python:3.11-slim RUN pip install -e ".[dev]" # Includes pytest, debug tools, etc.
✅ Separate dev and prod stages
FROM base AS development RUN pip install -e ".[dev]" FROM base AS production RUN pip install . # Prod deps only
❌ Running as root
CMD ["uvicorn", "app:main"] # Runs as root
✅ Create and use non-root user
RUN useradd -m app USER app CMD ["uvicorn", "app:main"]
❌ Copying before installing dependencies
COPY . . # Invalidates cache on every code change RUN pip install -r requirements.txt
✅ Install dependencies first
COPY requirements.txt . RUN pip install -r requirements.txt # Cached unless requirements.txt changes COPY . .
❌ Using :latest
tags
:latestFROM python:3-slim # Breaks reproducibility
✅ Pin to patch version
FROM python:3.11.11-slim
❌ Multiple apt-get updates
RUN apt-get update && apt-get install -y curl RUN apt-get update && apt-get install -y git # Second update is wasted
✅ Single apt layer
RUN apt-get update && apt-get install -y \ curl \ git \ && rm -rf /var/lib/apt/lists/*
Building Images
Enable BuildKit
export DOCKER_BUILDKIT=1 # Or in docker-compose.yml export COMPOSE_DOCKER_CLI_BUILD=1
Multi-platform builds
# For ARM64 Macs building x86_64 images docker buildx build \ --platform linux/amd64 \ --tag org/app:latest \ --push \ . # Build for both platforms docker buildx build \ --platform linux/amd64,linux/arm64 \ --tag org/app:latest \ --push \ .
Cache optimization
# Use inline cache for CI docker buildx build \ --cache-from type=registry,ref=org/app:buildcache \ --cache-to type=registry,ref=org/app:buildcache,mode=max \ --tag org/app:latest \ --push \ .
Additional Resources
- For detailed explanations and edge cases, see reference.md
- For complete working examples, see templates/
- For image size comparison and benchmarks, see optimization.md
When NOT to Use This Skill
- Creating
for simple local dev (just use official images)docker-compose.yml - Debugging running containers (use
,docker logs
instead)docker exec - Container orchestration at scale (Kubernetes/ECS patterns differ significantly)