.NETEasy
Docker Compose networking — service discovery, ports, and the bridge driver
Compose creates a user-defined bridge network per project. Every service is reachable by its service name as DNS.
Default behaviour
services:
api:
build: .
depends_on: [db]
environment:
DATABASE_URL: postgres://app:pw@db:5432/app # "db" resolves to the db container
db:
image: postgres:16
No port mapping needed for internal comms — db:5432 is reachable from api. Use ports: only to expose to the host.
Three port-related expressions
| Syntax | Means |
|---|---|
ports: ["5432:5432"] | host:container — accessible from host machine |
expose: ["5432"] | declared but only network-internal (not strictly required on Compose; documentation) |
no ports | service is only reachable from other containers on the same network |
Multiple networks for isolation
networks:
frontend:
backend:
services:
web:
networks: [frontend]
api:
networks: [frontend, backend]
db:
networks: [backend] # web cannot reach db; only api can
Health checks + depends_on with condition
depends_on only orders start, not ready. Add a healthcheck:
services:
db:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app"]
interval: 5s
retries: 10
api:
build: .
depends_on:
db:
condition: service_healthy
Reaching the host from a container
- Linux:
host.docker.internal(Compose v2+ resolves it). - Mac/Windows: same hostname works natively.
- Production: never rely on host networking — use a real service mesh.
Common debugging commands
docker compose ps # state + ports
docker compose logs -f api # tail logs
docker compose exec api ping db # service-name resolution
docker network inspect <project>_default
Production warning
Compose is dev / single-host. For prod, graduate to Kubernetes or ECS — service discovery, scaling, and rolling deploys are first-class there.