Container networking and service discovery

Each container has its own loopback; peers find each other by service name on a shared network.

A container gets its own network namespace, with its own interfaces, routing table, and loopback. Inside the Waybill container, localhost is Waybill. Ledger listening on 5432 in its container is invisible there, which is why a connection to localhost:5432 is refused even though "Ledger is definitely listening".

Compose, Docker networks, and Kubernetes solve discovery the same way: an embedded DNS server resolves service names to the current addresses of their containers. Connect to ledger:5432, not to an IP address. IPs change whenever a container is recreated, and a hardcoded address fails at the next deploy.

Keep peers private:

  • Services on the same network reach each other on container ports. Publishing a port (ports:) maps it onto the host, which is only needed for callers outside Docker. Publishing a database to make localhost work exposes it to anything that can reach the host.
  • internal: true networks have no route out. That is good for backends and for labs.
  • network_mode: service:x makes one container share another's namespace so localhost happens to work. It couples their lifecycles and hides the addressing mistake.

Twelve-Factor calls these peers backing services: attached resources located by configuration (a URL or hostname). You should be able to swap one without changing code.