Skip to content
Back to skills

Docker Best Practices

FSecurity

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.

  • 57 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added October 17, 2026
devopspythonbashnodenodejsfastapiflaskdockerkubernetesdebugginggit

Works with

  • vscode
  • cli
  • api

Security analysis

F31/100
  • criticalPipes output to a shell interpreter
  • mediumUses curl or wget to download content
  • criticalAccesses sensitive system or user directories
  • highPerforms destructive filesystem operations
  • criticalDownloads and executes remote scripts — classic supply chain attack
  • criticalDownloads and executes remote scripts — classic supply chain attack
  • mediumInstalls packages at runtime which could introduce malicious dependencies
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro shows the line behind each finding and how to fix it

Scanned October 17, 2026

npx -y skills add diegosouzapw/awesome-omni-skill --skill docker-best-practices --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Docker Best Practices?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Docker Best Practices
[![Security: F — Skills Directory](https://www.skillsdirectory.com/api/skills/diegosouzapw-docker-best-practices/badge)](https://www.skillsdirectory.com/skills/diegosouzapw-docker-best-practices)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: docker-best-practices
description: 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.
---

# 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

1. **Multi-stage builds** — Separate build dependencies from runtime
2. **Pinned versions** — Never use `:latest` tags
3. **Layer caching** — Order instructions from least to most frequently changing
4. **Minimal attack surface** — Distroless or slim base images, non-root users
5. **BuildKit features** — Cache mounts, secrets, multi-platform builds
6. **Fast startup** — Eager dependencies, lazy application code

## Quick Reference

### Python API Services (FastAPI, Flask)

```dockerfile
# 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)

```dockerfile
# 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

```dockerfile
# ─── 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

```dockerfile
# ❌ 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

```dockerfile
# ❌ 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

```dockerfile
# Install dependencies (rarely change) — cached ✅
COPY pyproject.toml ./
RUN pip install .

# Copy application code (changes often) — rebuilt on every change ✅
COPY . .
```

### Health Checks

```dockerfile
# 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

```dockerfile
# 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

```yaml
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
- [ ] `--mount=type=cache` for pip/npm/apt downloads
- [ ] Layer ordering: system packages → language runtime → dependencies → app code
- [ ] `.dockerignore` excludes unnecessary files
- [ ] Single `apt-get update && install && rm -rf` per stage (not multiple RUN commands)

### 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 `--mount=type=secret` instead)
- [ ] Health check defined

### Size
- [ ] `--no-install-recommends` on apt-get
- [ ] `rm -rf /var/lib/apt/lists/*` after apt-get
- [ ] `--no-cache-dir` on pip installs
- [ ] Multi-stage excludes build tools from final image
- [ ] Production stage only includes runtime dependencies

### Correctness
- [ ] `WORKDIR` set before COPY
- [ ] Correct user ownership for copied files if using non-root user
- [ ] `EXPOSE` matches actual port
- [ ] `CMD`/`ENTRYPOINT` is production-appropriate (no `--reload`)
- [ ] Environment variables have sensible defaults

## Common Mistakes to Avoid

### ❌ Installing dev dependencies in production
```dockerfile
FROM python:3.11-slim
RUN pip install -e ".[dev]"  # Includes pytest, debug tools, etc.
```

### ✅ Separate dev and prod stages
```dockerfile
FROM base AS development
RUN pip install -e ".[dev]"

FROM base AS production
RUN pip install .  # Prod deps only
```

### ❌ Running as root
```dockerfile
CMD ["uvicorn", "app:main"]  # Runs as root
```

### ✅ Create and use non-root user
```dockerfile
RUN useradd -m app
USER app
CMD ["uvicorn", "app:main"]
```

### ❌ Copying before installing dependencies
```dockerfile
COPY . .  # Invalidates cache on every code change
RUN pip install -r requirements.txt
```

### ✅ Install dependencies first
```dockerfile
COPY requirements.txt .
RUN pip install -r requirements.txt  # Cached unless requirements.txt changes
COPY . .
```

### ❌ Using `:latest` tags
```dockerfile
FROM python:3-slim  # Breaks reproducibility
```

### ✅ Pin to patch version
```dockerfile
FROM python:3.11.11-slim
```

### ❌ Multiple apt-get updates
```dockerfile
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
```dockerfile
RUN apt-get update && apt-get install -y \
    curl \
    git \
    && rm -rf /var/lib/apt/lists/*
```

## Building Images

### Enable BuildKit
```bash
export DOCKER_BUILDKIT=1

# Or in docker-compose.yml
export COMPOSE_DOCKER_CLI_BUILD=1
```

### Multi-platform builds
```bash
# 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
```bash
# 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](reference.md)
- For complete working examples, see [templates/](templates/)
- For image size comparison and benchmarks, see [optimization.md](optimization.md)

## When NOT to Use This Skill

- Creating `docker-compose.yml` for simple local dev (just use official images)
- Debugging running containers (use `docker logs`, `docker exec` instead)
- Container orchestration at scale (Kubernetes/ECS patterns differ significantly)

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.