Container networking and composition
How containers find each other by name, why `localhost` means "me", and how Compose wires a stack together.
Every container has its own localhost
A container's network namespace contains its own interfaces, its own routing table, its own ports, and its own loopback. localhost inside a container means that container. Code written on a laptop, where the API and database both ran as local processes, often says localhost:7000. Moved into two containers, the same code fails with "connection refused", which is exactly the loopback-only bind from the Networking for Operators track seen from the other side.
How Docker wires containers together
For each container, Docker creates a virtual Ethernet pair. One end becomes the container's eth0, and the other is attached to a bridge on the host. Containers on the same user-defined network can reach each other directly, and Docker runs an embedded DNS server (shown as 127.0.0.11 in the container's resolv.conf) that resolves service and container names to their current addresses. The legacy default bridge does not provide that name resolution, which is one reason to always use project networks.
Compose creates a network per project and attaches every service to it, so in a Compose file:
services:
api:
environment:
LEDGER_URL: http://ledger:7000
networks: [backend]
ledger:
networks: [backend]
networks:
backend:
internal: true
ledger resolves from api, the address may change on every recreate, and the name stays the same. internal: true gives the network no route to the outside, which is useful for back-end tiers.
Exposing versus publishing
- Container-to-container traffic on a shared network needs nothing extra. Any port the process listens on is reachable by peers.
- Publishing (
ports: ["8080:8080"]) maps a host port to a container port and makes the service reachable through the host's interfaces. Publish only what users or other hosts need, and bind it to a specific host address when you can (127.0.0.1:8080:8080). - Host networking removes the network namespace entirely. It is fast and simple, and it gives up isolation.
The Container Security chapter adds a security reading: every unnecessary network path is an unnecessary attack path. Segment tiers onto separate networks so that a compromised front end cannot talk to the database directly.
Backing services are configuration
Twelve-Factor calls databases, queues, and other services attached resources. The code holds no knowledge of where they are. It reads a URL from configuration. That one rule makes containers portable. The same image runs in Compose with http://ledger:7000, in Kubernetes with a Service name, and in production behind a managed endpoint. It also makes the compose-DNS lab's fix a configuration change rather than a code change.
Debugging connectivity between containers
docker compose exec api getent hosts ledger # does the name resolve?
docker compose exec api wget -qO- http://ledger:7000/health
docker network inspect <project>_backend # who is attached?
docker compose exec ledger ss -ltn # is Ledger bound to 0.0.0.0 or only 127.0.0.1?
Work through name, path, listener, and bind address, the same order the Networking for Operators track taught, inside the container's own namespace.
Key terms
- Network namespace
- A private network stack (interfaces, routes, ports, and its own loopback) for a container.
- User-defined network
- A Docker network created for a project. Containers on it resolve each other by service or container name through embedded DNS.
- Publishing a port
- Mapping a host port to a container port (
-p 8080:8080), which makes the service reachable from outside the host. - Internal network
- A network with no route outside its members. Compose marks it with
internal: true.
Read further
- Container Security, Chapter "Container Network Security" (Purchase)
Container networking models, network namespaces and virtual Ethernet pairs, and the advice on limiting which containers can talk to which. - The Twelve-Factor App, Factors IV "Backing services" and VII "Port binding" (Free online)
Treating a database or another service as an attached resource located by configuration, and a service that exports itself by binding a port. - UNIX and Linux System Administration Handbook, 5th edition, Ch. 25, "Containers" (Purchase)
The Docker networking section, especially bridge networks and port publishing.