Skip to main content

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

ServiceHealth Check Command
PostgreSQLpg_isready -U postgres
MySQLmysqladmin ping -h localhost
Redisredis-cli ping
HTTP servicecurl -f http://localhost:PORT/health
MongoDBmongosh --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 -d without --force-recreate unless 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

IssueFix
Slow container startupAdd health checks with start_period
Services fail because DB isn't readyUse depends_on with condition: service_healthy
Containers on wrong networkUse explicit networks: in Compose
Internal service exposed to internetUse internal: true on backend network
Debug tools always runningUse profiles: to make them opt-in
Rebuilds when nothing changedPin 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_on with service_healthy to 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