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

283 lines
5.7 KiB
Markdown

# Docker Compose Patterns Reference
## Production-Ready Patterns
### Web App + Database + Cache
```yaml
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
env_file:
- .env
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 3s
retries: 3
start_period: 10s
restart: unless-stopped
networks:
- frontend
- backend
mem_limit: 512m
cpus: 1.0
db:
image: postgres:16-alpine
volumes:
- pgdata:/var/lib/postgresql/data
env_file:
- .env.db
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
networks:
- backend
mem_limit: 256m
redis:
image: redis:7-alpine
command: redis-server --maxmemory 64mb --maxmemory-policy allkeys-lru
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 3
restart: unless-stopped
networks:
- backend
mem_limit: 128m
volumes:
pgdata:
networks:
frontend:
backend:
internal: true
```
### Key Patterns
- **Healthchecks on every service** — enables depends_on with condition
- **Named volumes** — data persists across container recreation
- **Explicit networks** — backend is internal (no external access)
- **env_file** — secrets not in compose file
- **Resource limits** — prevent runaway containers
---
## Development Override Pattern
### docker-compose.yml (base — production-like)
```yaml
services:
app:
build: .
ports:
- "3000:3000"
restart: unless-stopped
```
### docker-compose.override.yml (dev — auto-loaded)
```yaml
services:
app:
build:
target: development
volumes:
- .:/app # Bind mount for hot reload
- /app/node_modules # Preserve container node_modules
environment:
- NODE_ENV=development
- DEBUG=true
ports:
- "9229:9229" # Debug port
restart: "no"
```
### Usage
```bash
# Development (auto-loads override)
docker compose up
# Production (skip override)
docker compose -f docker-compose.yml up -d
# Explicit profiles
docker compose --profile dev up
docker compose --profile prod up -d
```
---
## Network Isolation Pattern
```yaml
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
networks:
- frontend
app:
build: .
networks:
- frontend
- backend
db:
image: postgres:16-alpine
networks:
- backend
redis:
image: redis:7-alpine
networks:
- backend
networks:
frontend:
# External traffic reaches nginx and app
backend:
internal: true
# DB and Redis only reachable by app
```
### Why This Matters
- Database and cache are **not accessible from outside**
- Only nginx and app handle external traffic
- Lateral movement limited if one container is compromised
---
## Worker + Queue Pattern
```yaml
services:
api:
build:
context: .
target: runtime
command: uvicorn main:app --host 0.0.0.0 --port 8000
ports:
- "8000:8000"
depends_on:
rabbitmq:
condition: service_healthy
worker:
build:
context: .
target: runtime
command: celery -A tasks worker --loglevel=info
depends_on:
rabbitmq:
condition: service_healthy
scheduler:
build:
context: .
target: runtime
command: celery -A tasks beat --loglevel=info
depends_on:
rabbitmq:
condition: service_healthy
rabbitmq:
image: rabbitmq:3.13-management-alpine
ports:
- "15672:15672" # Management UI (dev only)
healthcheck:
test: ["CMD", "rabbitmq-diagnostics", "check_running"]
interval: 10s
timeout: 5s
retries: 5
```
---
## Logging Configuration
```yaml
services:
app:
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
tag: "{{.Name}}/{{.ID}}"
```
### Why
- **max-size** prevents disk exhaustion
- **max-file** rotates logs automatically
- Default Docker logging has NO size limit — production servers can run out of disk
---
## Environment Variable Patterns
### .env.example (committed to repo)
```env
# Database
DATABASE_URL=postgres://user:password@db:5432/appname
POSTGRES_USER=user
POSTGRES_PASSWORD=changeme
POSTGRES_DB=appname
# Redis
REDIS_URL=redis://redis:6379/0
# Application
SECRET_KEY=changeme-generate-a-real-secret
NODE_ENV=production
LOG_LEVEL=info
# External Services (BYOK)
# SMTP_HOST=
# SMTP_PORT=587
# AWS_ACCESS_KEY_ID=
# AWS_SECRET_ACCESS_KEY=
```
### Variable Substitution in Compose
```yaml
services:
app:
image: myapp:${APP_VERSION:-latest}
environment:
- LOG_LEVEL=${LOG_LEVEL:-info}
- PORT=${PORT:-3000}
```
---
## Troubleshooting Checklist
| Symptom | Likely Cause | Fix |
|---------|-------------|-----|
| Container exits immediately | CMD/ENTRYPOINT crashes, missing env vars | Check logs: `docker compose logs service` |
| Port already in use | Another service or host process on same port | Change host port: `"3001:3000"` |
| Volume permissions denied | Container user doesn't own mounted path | Match UID/GID or use named volumes |
| Build cache not working | COPY . . invalidates cache early | Reorder: copy deps first, then source |
| depends_on doesn't wait | No healthcheck condition | Add `condition: service_healthy` |
| Container OOM killed | No memory limit or limit too low | Set appropriate `mem_limit` |
| Network connectivity issues | Wrong network or service name | Services communicate by service name within shared network |