Docker Compose Best Practices for Self-Hosting
Docker Compose is the backbone of self-hosted stacks. These best practices keep your services reliable, secure, and maintainable.
Use Specific Image Tags
Never use :latest in production.
Bad: image: postgres:latest
Good: image: postgres:16.2-alpine
Pinned versions mean predictable behavior. You update when you choose to, not when upstream pushes a breaking change.
Restart Policies
Always set a restart policy:
restart: unless-stopped
Options:
For most self-hosted apps: unless-stopped.
Health Checks
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 60s
Health checks tell you (and other services) when an app is actually ready.
Network Isolation
Don't put everything on the default network. Create separate networks:
networks:
frontend:
backend:
services:
app:
networks: [frontend, backend]
database:
networks: [backend]
caddy:
networks: [frontend]
The database is only reachable by the app, not by the reverse proxy.
Environment Variables
Don't hardcode secrets in docker-compose.yml.
Use .env files:
env_file:
Or Docker secrets for sensitive values.
Resource Limits
deploy:
resources:
limits:
memory: 512M
cpus: '0.5'
Prevent a single runaway container from consuming all server resources.
Logging
Configure log rotation to prevent disk exhaustion:
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
Without this, a chatty application can fill your disk.
Volume Organization
Use named volumes with clear names:
volumes:
postgres_data:
app_uploads:
redis_data:
Never use anonymous volumes in production.
Ordering with depends_on
depends_on:
db:
condition: service_healthy
Combine depends_on with health checks so your app waits for the database to be actually ready, not just started.