Network and Compose Optimization
Poor network topology and Compose configuration cause slow startups, unnecessary exposure, and fragile deployments. This lesson covers how to design lean, reliable stacks.
Network Topology Optimization
Use Explicit Networks Instead of Default Bridge
The default bridge network does not provide DNS-based service discovery. Always create explicit networks:
services:
api:
networks: [back]
db:
networks: [back]
networks:
back: {}
Segment Networks by Function
Only connect services that need to communicate:
flowchart TD
Internet --> Proxy["Reverse Proxy"]
subgraph front ["Frontend Network"]
Proxy --> Web["Web App"]
end
subgraph back ["Backend Network"]
Web --> API["API Server"]
API --> DB["Database"]
end
style front fill:#e3f2fd,stroke:#1565c0
style back fill:#fff3e0,stroke:#ef6c00
services:
proxy:
networks: [front]
web:
networks: [front, back]
api:
networks: [back]
db:
networks: [back]
networks:
front: {}
back: {}
The database is never reachable from the frontend network.
Mark Internal-Only Networks
networks:
back:
internal: true # No outbound internet access
Services on internal networks cannot reach the internet, reducing exfiltration risk.
Compose Startup Optimization
Use Health Checks with depends_on
Without health checks, depends_on only waits for the container to start, not for the service to be ready:
services:
api:
depends_on:
db:
condition: service_healthy
db:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 5
Common Health Check Commands
| Service | Health Check Command |
|---|---|
| PostgreSQL | pg_isready -U postgres |
| MySQL | mysqladmin ping -h localhost |
| Redis | redis-cli ping |
| HTTP service | curl -f http://localhost:PORT/health |
| MongoDB | mongosh --eval "db.runCommand('ping')" |
Compose Configuration Optimization
Keep Override Layers Minimal
compose.yaml ← Base configuration (all environments)
compose.override.yaml ← Local dev overrides (auto-loaded)
compose.prod.yaml ← Production overrides (explicit -f)
Only override what changes between environments (ports, resource limits, volumes).
Avoid Redundant Service Restarts
Compose only recreates containers whose configuration changed. To take advantage of this:
- Pin image tags (don't use
latest) - Don't change environment variables unnecessarily
- Use
docker compose up -dwithout--force-recreateunless needed
Use Profiles for Optional Services
services:
api:
image: my-api:1.0.0
debug-tools:
image: nicolaka/netshoot
profiles: [debug] # Only starts with --profile debug
prometheus:
image: prom/prometheus
profiles: [monitoring]
# Normal start (debug-tools and prometheus NOT started)
docker compose up -d
# Start with monitoring
docker compose --profile monitoring up -d
Performance Quick Wins
| Issue | Fix |
|---|---|
| Slow container startup | Add health checks with start_period |
| Services fail because DB isn't ready | Use depends_on with condition: service_healthy |
| Containers on wrong network | Use explicit networks: in Compose |
| Internal service exposed to internet | Use internal: true on backend network |
| Debug tools always running | Use profiles: to make them opt-in |
| Rebuilds when nothing changed | Pin image tags, avoid --force-recreate |
Key Takeaways
- Use explicit networks -- never rely on the default bridge for production.
- Segment networks by function so internal services are not exposed.
- Use
depends_onwithservice_healthyto ensure proper startup ordering. - Keep override layers minimal -- only override what changes between environments.
- Use profiles for optional services like debug tools and monitoring.
What's Next
- Continue to Module 12: Strategy for automation and deployment scripts.