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>
5.1 KiB
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.
Combine Related Commands
# 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