@tank/docker-production-patterns
1.0.0Description
Production Docker patterns covering Dockerfiles, multi-stage builds, BuildKit, security hardening, Compose production usage, lifecycle/signals, logging, and CI/CD.
Triggered by
Dockerfiledocker productionBuildKitdocker securityhealth checkdistroless
Download
Unsafe
tank install @tank/docker-production-patternsDocker Production Patterns
Core Philosophy
- Ship the minimum viable image — Every unnecessary binary, library, and shell in the final image is attack surface and wasted bandwidth. Multi-stage builds exist to separate build-time from run-time.
- Layers are the caching unit — Order instructions from least-changing (base image, system deps) to most-changing (application code). A single misordered COPY invalidates every subsequent layer.
- Containers are ephemeral — Design for termination. Handle SIGTERM, drain connections, flush buffers. If your container cannot stop cleanly in 10 seconds, the architecture is wrong.
- Security is not optional — Run as non-root, use read-only filesystems, scan images in CI, never bake secrets into layers. The default Docker setup is insecure for production.
- Compose is not just for dev — With profiles, resource limits, health checks, and secrets, Compose serves single-host production. Know when to graduate to orchestrators.
Quick-Start: Common Problems
"My Docker image is too large"
- Switch to multi-stage build — separate builder from runtime
- Use minimal base:
*-slim,*-alpine, or distroless - Audit .dockerignore — exclude
node_modules/,.git/, tests, docs - Combine RUN commands to reduce layers; clean package caches in same layer
- Copy only production artifacts into the final stage
-> See
references/dockerfile-patterns.md
"Builds are slow in CI"
- Enable BuildKit (
DOCKER_BUILDKIT=1) - Use
--mount=type=cachefor package manager caches (npm, pip, go mod) - Order COPY instructions: lockfile first, install, then source code
- Export/import cache with
--cache-toand--cache-fromin CI -> Seereferences/buildkit-optimization.md
"How do I handle secrets during build?"
- Use BuildKit secret mounts:
--mount=type=secret,id=mykey - Pass at build time:
docker build --secret id=mykey,src=./key.pem - Never use
ARGorENVfor secrets — they persist in image layers -> Seereferences/buildkit-optimization.mdandreferences/security-hardening.md
"Container ignores SIGTERM / takes 10s to stop"
- Use exec form for ENTRYPOINT:
["node", "server.js"]notnode server.js - Ensure your process is PID 1 (or use
--init/ tini) - Register a SIGTERM handler in your application code
-> See
references/lifecycle-signals.md
"How do I run Compose in production?"
- Use override files:
docker-compose.yml(base) +docker-compose.prod.yml - Set resource limits (memory, CPU), restart policies, health checks
- Use
docker compose --profile prod upfor environment-specific services - Manage secrets via Compose secrets, not environment variables
-> See
references/compose-production.md
Decision Trees
Base Image Selection
| Requirement | Base Image |
|---|---|
| Smallest size, maximum security | Distroless (gcr.io/distroless) or scratch |
| Need shell for debugging | *-slim variants (debian-slim, python-slim) |
| Alpine ecosystem / musl acceptable | *-alpine (watch for DNS/musl issues) |
| Enterprise compliance / support | Docker Official Images, Chainguard |
| Widest compatibility | Default Debian-based tags |
When to Graduate from Compose
| Signal | Recommendation |
|---|---|
| Single host, < 10 services | Docker Compose is fine |
| Multi-host, high availability needed | Kubernetes or Docker Swarm |
| Auto-scaling required | Kubernetes |
| Rolling updates with zero downtime | Kubernetes or Swarm |
| Simple deploy, one server | Compose + systemd |
Image Tagging Strategy
| Tag | Purpose | Mutable? |
|---|---|---|
v1.2.3 | Release artifact, immutable reference | No |
sha-abc1234 | Git SHA, CI traceability | No |
latest | Convenience for dev, never for production deploys | Yes |
main | Latest from default branch | Yes |
Language-Specific Quick Reference
| Language | Base (build) | Base (runtime) | Key gotcha |
|---|---|---|---|
| Node.js | node:22-slim | node:22-slim or distroless | Copy package*.json first, npm ci --omit=dev |
| Python | python:3.12-slim | python:3.12-slim | Use --mount=type=cache,target=/root/.cache/pip |
| Go | golang:1.23 | scratch or distroless | CGO_ENABLED=0 for static binary |
| Rust | rust:1.82 | debian:bookworm-slim or distroless | Use cargo-chef for dependency caching |
| Java | eclipse-temurin:21-jdk | eclipse-temurin:21-jre-alpine | Use jlink for custom minimal JRE |
-> See references/dockerfile-patterns.md for complete Dockerfiles per language.
Reference Index
| File | Contents |
|---|---|
references/dockerfile-patterns.md | Multi-stage builds, layer ordering, ENTRYPOINT vs CMD, base image selection, .dockerignore patterns, language-specific production Dockerfiles (Node.js, Python, Go, Rust, Java) |
references/buildkit-optimization.md | BuildKit cache mounts, build secrets, SSH forwarding, multi-platform builds, cache export/import, CI cache backends, build arguments |
references/security-hardening.md | Non-root users, distroless and minimal images, read-only filesystems, image scanning (Trivy, Grype, Snyk), secrets management, CIS benchmark essentials |
references/compose-production.md | Compose v2 production configuration, profiles, service dependencies, override files, secrets, resource limits, networking, environment management |
references/lifecycle-signals.md | PID 1 problem, SIGTERM/SIGKILL handling, tini and dumb-init, exec form vs shell form, health checks, restart policies, graceful shutdown patterns per language |
references/logging-observability.md | Log drivers (json-file, fluentd, syslog), log rotation, structured logging, resource limits (CPU/memory/pids), monitoring patterns, OOM behavior |
references/cicd-registry.md | GitHub Actions Docker builds, docker/metadata-action tagging, registry management (Docker Hub, GHCR, ECR), multi-arch builds, layer caching in CI, image promotion workflows |