Files
Leo bf1473b1be feat(skills): add research-summarizer and docker-development agent skills
research-summarizer (product-team/):
- Structured research summarization for papers, articles, reports
- Slash commands: /research:summarize, /research:compare, /research:cite
- Python tools: extract_citations.py (5 citation formats), format_summary.py (6 templates)
- References: summary-templates.md, citation-formats.md

docker-development (engineering/):
- Dockerfile optimization, compose orchestration, container security
- Slash commands: /docker:optimize, /docker:compose, /docker:security
- Python tools: dockerfile_analyzer.py (15 rules), compose_validator.py (best practices)
- References: dockerfile-best-practices.md, compose-patterns.md

Both skills include .claude-plugin/plugin.json and follow POWERFUL tier conventions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 22:47:16 +01:00

5.1 KiB

Dockerfile Best Practices Reference

Layer Optimization

The Golden Rule

Every RUN, COPY, and ADD instruction creates a new layer. Fewer layers = smaller image.

# Bad — 3 layers
RUN apt-get update
RUN apt-get install -y curl git
RUN rm -rf /var/lib/apt/lists/*

# Good — 1 layer
RUN apt-get update && \
    apt-get install -y --no-install-recommends curl git && \
    rm -rf /var/lib/apt/lists/*

Order Layers by Change Frequency

# Least-changing layers first
COPY package.json package-lock.json ./    # Changes rarely
RUN npm ci                                 # Changes when deps change
COPY . .                                   # Changes every build
RUN npm run build                          # Changes every build

Use .dockerignore

.git
node_modules
__pycache__
*.pyc
.env
.env.*
dist
build
*.log
.DS_Store
.vscode
.idea
coverage
.pytest_cache

Base Image Selection

Size Comparison (approximate)

Base Size Use Case
scratch 0MB Static binaries (Go, Rust)
distroless/static 2MB Static binaries with CA certs
alpine 7MB Minimal Linux, shell access
distroless/base 20MB Dynamic binaries (C/C++)
debian-slim 80MB When you need glibc + apt
ubuntu 78MB Full Ubuntu ecosystem
python:3.12-slim 130MB Python apps (production)
node:20-alpine 130MB Node.js apps
golang:1.22 800MB Go build stage only
python:3.12 900MB Never use in production
node:20 1000MB Never use in production

When to Use Alpine

  • Small image size matters
  • No dependency on glibc (musl works)
  • Willing to handle occasional musl-related issues
  • Not running Python with C extensions that need glibc

When to Use Slim

  • Need glibc compatibility
  • Python with compiled C extensions (numpy, pandas)
  • Fewer musl compatibility issues
  • Still much smaller than full images

When to Use Distroless

  • Maximum security (no shell, no package manager)
  • Compiled/static binaries
  • Don't need debugging access inside container
  • Production-only (not development)

Multi-Stage Builds

Why Multi-Stage

  • Build tools and source code stay out of production image
  • Final image contains only runtime artifacts
  • Dramatically reduces image size and attack surface

Naming Stages

FROM golang:1.22 AS builder     # Named stage
FROM alpine:3.19 AS runtime     # Named stage
COPY --from=builder /app /app   # Reference by name

Selective Copy

# Only copy the built artifact — nothing else
COPY --from=builder /app/server /server
COPY --from=builder /app/config.yaml /config.yaml
# Don't COPY --from=builder /app/ /app/ (copies source code too)

Security Hardening

Run as Non-Root

# Create user
RUN groupadd -r appgroup && useradd -r -g appgroup -s /sbin/nologin appuser

# Set ownership
COPY --chown=appuser:appgroup . .

# Switch user (after all root-requiring operations)
USER appuser

Secret Management

# Bad — secret baked into layer
ENV API_KEY=sk-12345

# Good — BuildKit secret mount (never in layer)
RUN --mount=type=secret,id=api_key \
    export API_KEY=$(cat /run/secrets/api_key) && \
    ./configure --api-key=$API_KEY

Build with:

docker build --secret id=api_key,src=./api_key.txt .

Read-Only Filesystem

# docker-compose.yml
services:
  app:
    read_only: true
    tmpfs:
      - /tmp
      - /var/run

Drop Capabilities

services:
  app:
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE  # Only if binding to ports < 1024

Build Performance

BuildKit Cache Mounts

# Cache pip downloads across builds
RUN --mount=type=cache,target=/root/.cache/pip \
    pip install -r requirements.txt

# Cache apt downloads
RUN --mount=type=cache,target=/var/cache/apt \
    apt-get update && apt-get install -y curl

Parallel Builds

# These stages build in parallel when using BuildKit
FROM node:20-alpine AS frontend
COPY frontend/ .
RUN npm ci && npm run build

FROM golang:1.22 AS backend
COPY backend/ .
RUN go build -o server

FROM alpine:3.19
COPY --from=frontend /dist /static
COPY --from=backend /server /server

Enable BuildKit

export DOCKER_BUILDKIT=1
docker build .

# Or in daemon.json
{ "features": { "buildkit": true } }

Health Checks

HTTP Service

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:8000/health || exit 1

Without curl (using wget)

HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:8000/health || exit 1

TCP Check

HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
  CMD nc -z localhost 8000 || exit 1

PostgreSQL

HEALTHCHECK --interval=10s --timeout=5s --retries=5 \
  CMD pg_isready -U postgres || exit 1

Redis

HEALTHCHECK --interval=10s --timeout=3s --retries=3 \
  CMD redis-cli ping | grep PONG || exit 1