Container networking

Docker · Practical · 12 min

Multi-container apps (app + database + cache) depend on containers finding each other reliably.

Docker gives containers networking through network drivers:

  • bridge: a private virtual network on one host. The default for containers.
  • host: the container uses the host's network stack directly, with no isolation and no port mapping. Linux only, and occasionally used for performance.
  • none: no networking at all.
  • overlay (Swarm) and macvlan/ipvlan cover multi-host and LAN-attached setups.

Containers started without --network join the default bridge, where they can reach each other only by IP address. Don't use it for apps. Create a user-defined bridge network instead:

docker network create appnet
docker run -d --name db --network appnet postgres:17
docker run -d --name api --network appnet myapi

On a user-defined network, Docker's embedded DNS resolves container names (and --network-alias names) to their current IPs, so api connects to postgres://db:5432. IPs change when containers are recreated; names don't. Networks also isolate: containers on different networks can't talk unless one is attached to both (docker network connect).

Notice there's no -p on the database: container-to-container traffic flows over the network on the container's real port. Publish only what the outside world needs, such as the API or a reverse proxy.

Reaching the host from a container: Docker Desktop provides host.docker.internal; on Linux add --add-host=host.docker.internal:host-gateway.

docker network inspect appnet lists connected containers and their IPs. Docker Compose creates a network per project automatically, which is why services there can reach each other by service name.

Example

docker network create appnet
docker run -d --name db --network appnet -e POSTGRES_PASSWORD=dev postgres:17
docker run -d --name api --network appnet -p 8000:8000 \
  -e DATABASE_URL=postgres://postgres:dev@db:5432/postgres myapi:1.0

# Name resolution from inside the network
docker run --rm --network appnet alpine nslookup db
# Name: db  Address: 172.18.0.2

docker network inspect appnet --format '{{range .Containers}}{{.Name}} {{end}}'
# db api

# Reach a service running on the host (Linux)
docker run --rm --add-host=host.docker.internal:host-gateway alpine \
  ping -c1 host.docker.internal

Remember

  • Use user-defined bridge networks; the default bridge has no name DNS
  • Containers on the same network reach each other by name on container ports
  • Only publish ports for traffic from outside Docker
  • host.docker.internal reaches the host (host-gateway on Linux)

Watch out for

  • Hard-coding container IP addresses — they change whenever containers are recreated.

Check yourself

  1. api and db are both on user-defined network appnet; db has no -p flag. Can api connect to db:5432?
  2. Two containers on the default bridge network try to reach each other by name. What happens?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.docker.com/engine/network/