| name | docker-patterns |
| description | Docker and Docker Compose patterns for local development and production — multi-stage Dockerfiles, Compose service orchestration, networking, volume strategies, container security hardening, secret management, debugging, and anti-patterns. Use this skill whenever setting up Docker Compose for local dev, writing or reviewing a Dockerfile, designing a multi-container architecture, troubleshooting container networking or volume issues, migrating a project to containers, or when the user says "dockerize this", "docker compose", "multi-stage build", "container security", "why is my container broken", or mentions volumes, networks, healthchecks, or deployment stages. Do NOT trigger for Kubernetes manifests (different skill) or for the Kadmon harness itself, which runs on the host. |
Docker Patterns
Practical Docker and Docker Compose patterns for containerized development and production. Covers the decisions you actually face: which Compose file to write, how to stage the Dockerfile, where to put secrets, how to debug a broken container.
When to Activate
- Setting up Docker Compose for local development
- Designing a multi-container architecture (app + db + cache + mail + etc.)
- Writing or reviewing a multi-stage Dockerfile
- Troubleshooting networking, volumes, or service discovery
- Reviewing a Dockerfile for security and image size
- Migrating from local-process dev to containerized workflow
Docker Compose for Local Development
Standard Web App Stack
services:
app:
build:
context: .
target: dev
ports:
- "3000:3000"
volumes:
- .:/app
- /app/node_modules
environment:
- DATABASE_URL=postgres://postgres:postgres@db:5432/app_dev
- REDIS_URL=redis://redis:6379/0
- NODE_ENV=development
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
command: npm run dev
db:
image: postgres:16-alpine
ports:
- "5432:5432"
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: app_dev
volumes:
- pgdata:/var/lib/postgresql/data
- ./scripts/init-db.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 5
redis:
image: redis:7-alpine
volumes:
- redisdata:/data
mailpit:
image: axllent/mailpit
ports:
- "8025:8025"
- "1025:1025"
volumes:
pgdata:
redisdata:
Multi-Stage Dockerfile — dev + build + production
# Stage 1 — deps
FROM node:22-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
# Stage 2 — dev (hot reload, debug tools)
FROM node:22-alpine AS dev
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
EXPOSE 3000
CMD ["npm", "run", "dev"]
# Stage 3 — build
FROM node:22-alpine AS build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build && npm prune --production
# Stage 4 — production (minimal image, non-root)
FROM node:22-alpine AS production
WORKDIR /app
RUN addgroup -g 1001 -S appgroup && adduser -S appuser -u 1001
USER appuser
COPY --from=build --chown=appuser:appgroup /app/dist ./dist
COPY --from=build --chown=appuser:appgroup /app/node_modules ./node_modules
COPY --from=build --chown=appuser:appgroup /app/package.json ./
ENV NODE_ENV=production
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s CMD wget -qO- http://localhost:3000/health || exit 1
CMD ["node", "dist/server.js"]
Override Files — dev and prod from one base
services:
app:
environment:
- DEBUG=app:*
- LOG_LEVEL=debug
ports:
- "9229:9229"
services:
app:
build:
target: production
restart: always
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M
docker compose up
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
Networking
Services in the same Compose network resolve by service name:
# From the "app" container:
postgres://postgres:postgres@db:5432/app_dev # "db" resolves to the db container
redis://redis:6379/0 # "redis" resolves to the redis container
Custom networks for isolation
services:
frontend:
networks: [frontend-net]
api:
networks: [frontend-net, backend-net]
db:
networks: [backend-net]
networks:
frontend-net:
backend-net:
Expose only what you need
services:
db:
ports:
- "127.0.0.1:5432:5432"
Volume Strategies
volumes:
pgdata:
volumes:
- .:/app
- /app/node_modules
- /app/.next
- pgdata:/var/lib/postgresql/data
- ./scripts/init.sql:/docker-entrypoint-initdb.d/init.sql
Container Security
Dockerfile hardening
- Pin to a specific tag, never
:latest
- Run as a non-root user (
adduser -S, USER)
- Use
alpine or distroless for smaller attack surface
- No secrets in image layers — ever
Compose hardening
services:
app:
security_opt:
- no-new-privileges:true
read_only: true
tmpfs:
- /tmp
- /app/.cache
cap_drop: [ALL]
cap_add:
- NET_BIND_SERVICE
Secret Management
services:
app:
env_file: [.env]
environment:
- API_KEY
secrets:
db_password:
file: ./secrets/db_password.txt
services:
db:
secrets: [db_password]
.dockerignore
node_modules
.git
.env
.env.*
dist
coverage
*.log
.next
.cache
docker-compose*.yml
Dockerfile*
README.md
tests/
Debugging
docker compose logs -f app
docker compose logs --tail=50 db
docker compose exec app sh
docker compose exec db psql -U postgres
docker compose ps
docker compose top
docker stats
docker compose up --build
docker compose build --no-cache app
docker compose down
docker compose down -v
docker system prune
Network debugging
docker compose exec app nslookup db
docker compose exec app wget -qO- http://api:3000/health
docker network ls
docker network inspect <project>_default
Anti-Patterns
- Using
docker compose alone in production — use Kubernetes, ECS, or Docker Swarm for production orchestration
- Storing data in containers without volumes — containers are ephemeral; all data vanishes on restart
- Running as root — always create and use a non-root user
- Using
:latest — pin to specific versions for reproducible builds
- One giant container with every service — one process per container
- Secrets in
docker-compose.yml — use .env (gitignored) or Docker secrets
Integration
- arkitect agent (opus) — primary owner. arkitect handles architecture and deployment design decisions; this skill is the containerization playbook it reaches for when the design question is "how do we run this reliably in containers".
- api-design skill — related.
api-design covers the HTTP surface of the service; docker-patterns covers how to run and isolate that service.
- security-review skill — complementary. The "Container Security" section here overlaps with the secrets-management and dependency-security sections of
security-review; load both when reviewing a production Dockerfile.
- /abra-kdabra command — entry point. When planning a containerization or deployment change, arkitect loads this skill during the planning phase.
no_context Application
Docker recommendations must map to the real project — the actual services, the actual ports, the actual database. Before suggesting "add a healthcheck to your db service", verify the project has a db service and read the Compose file to see whether one already exists. Before recommending a multi-stage Dockerfile, read the existing Dockerfile if any — often the fix is a three-line edit, not a full rewrite. The no_context principle here means: read the actual Dockerfile, docker-compose.yml, and .dockerignore before making any structural recommendation.