Course outline · 0% complete

0/29 lessons0%

Course overview →

Container networking

lesson 4-2 · ~13 min · 12/29

How containers talk to each other

A real app is usually at least two containers, an app and a database. But each container has its own isolated network world, its own localhost. If the app connects to localhost:5432, it finds nothing, because its localhost is itself, not the database container.

The answer is a user-defined network:

docker network create mynet
docker run -d --name db --network mynet postgres:16
docker run -d --name app --network mynet -p 8080:8000 myapp:v1

Containers on the same network get two things:

  1. They can reach each other's ports directly, no -p needed between them
  2. DNS by container name: the app connects to host db, port 5432, and Docker resolves db to the right container

So the app's database config is simply host=db port=5432. The only -p left is for you, the outside world, reaching the app.

network "mynet"appconnects to db:5432dbpostgres on 5432on a shared network, containers reach each other by container name
Two containers on the network mynet. The app addresses the database by its container name, db, and Docker's built-in DNS routes the traffic.

The default bridge trap

Containers started without --network land on Docker's built-in default network, named bridge, and it differs in the one way that matters: no name-based DNS.

On the default bridge, db does not resolve to anything, containers must find each other by IP address, and container IPs change on every restart. That is why every real setup creates its own network, since name resolution is the feature being opted into.

docker network ls             # all networks on this machine
docker network inspect mynet  # which containers are attached, and their IPs
NetworkDNS by container name
default bridgeno
user-defined, docker network create mynetyes

Two further facts are worth knowing. Containers can reach the internet by default, so pulling packages works out of the box, and the isolation is about what can reach them plus container-to-container discovery. One container can also join several networks, which teams use to keep a database reachable from the app but not from the public-facing proxy.

Why localhost fails between containers

When app and db are both on mynet and the app reaches for Postgres at localhost:5432, it fails because inside the app container localhost means the app container itself. The fix is to connect to the hostname db.

Every container has its own isolated localhost, so on a shared user-defined network the right hostname is the container name.

Address from inside appReaches
localhost:5432the app container itself
db:5432the database container

Publishing a port with -p does not help here either, because -p is only for reaching containers from outside Docker. Container-to-container traffic on a shared network needs no published ports at all.

An unresolvable container hostname

When app and db start without any --network flag and the app cannot resolve the hostname db, both containers landed on the default bridge network, which does not provide DNS by container name. The fix is to create a user-defined network.

Name-based DNS is a feature of user-defined networks only, and on the default bridge containers see each other by IP at best.

docker network create mynet
docker run -d --name db --network mynet postgres:16
docker run -d --name app --network mynet myapp:v1
Setupdb resolves
no --network flagno
--network mynet on bothyes

Compose does all of this automatically in the next unit, which is a large part of why teams stop writing docker network create by hand.

The hostname a connection string uses

With the database started as --name db --network mynet and the app on the same network, the hostname is db, exactly what was passed to --name.

On a user-defined network, Docker runs a small DNS service that maps each container's name to its address, so a connection string works with no IP addresses anywhere:

postgres://user:pass@db:5432/shop
PieceSource
dbthe container's --name
5432the port inside that container
shopthe database name

This is worth remembering, because it is exactly how services find each other in docker compose next unit. The service name in the compose file becomes the hostname, with no extra configuration.