The core idea
A user-defined bridge is a bridge network you create with docker network create. It works like the default one,
plus two things that matter: containers on it can find each other by name, through DNS built into Docker, and
it's isolated from containers on other networks.
Names, not IPs
$ docker network create appnet$ docker run -d --name web1 --network appnet nginx:alpine$ docker run --rm --network appnet alpine sh -c 'cat /etc/resolv.conf; nslookup web1; wget -qO- http://web1'nameserver 127.0.0.11options ndots:0Name: web1Address: 172.20.0.2<title>Welcome to nginx!</title>
Output trimmed. Compare resolv.conf with the default bridge's (05.01 How container networking works): on a user-defined network, every
container asks 127.0.0.11, Docker's embedded DNS server. It answers with the current IP of any container on the
same network, by name, and forwards everything else to the normal resolver.
Recreate web1 and it may get a new IP, but http://web1 keeps working.
Aliases
A container can answer to extra names with --network-alias. Give several containers the same alias and DNS returns
all of them:
$ docker run -d --name api1 --network appnet --network-alias api alpine sleep 300$ docker run -d --name api2 --network appnet --network-alias api alpine sleep 300$ docker run --rm --network appnet alpine nslookup apiAddress: 172.20.0.4Address: 172.20.0.3
Clients pick one of the addresses, which gives you crude load-spreading. It's a DNS trick, not a load balancer: there are no health checks, and clients often cache the answer.
Isolation between networks
Containers on different user-defined networks can't reach each other, not even by IP:
$ docker network create frontend$ docker network create backend$ docker run -d --name dbx --network backend nginx:alpine$ docker run --rm --network frontend alpine wget -qO- -T 3 http://dbxwget: bad address 'dbx'$ docker run --rm --network frontend alpine wget -qO- -T 3 http://172.22.0.2wget: download timed out
The name doesn't resolve, and the IP isn't routable from the other network. That's the basis of a simple, useful security layout: a public-facing proxy on one network, the database on another, and only the app on both.
One container, several networks
docker network connect attaches a running container to another network. It gets a second interface:
$ docker run -d --name bridgebox --network frontend alpine sleep 300$ docker network connect backend bridgebox$ docker exec bridgebox ip -4 addr | grep inetinet 127.0.0.1/8 scope host loinet 172.21.0.2/16 brd 172.21.255.255 scope global eth0inet 172.22.0.3/16 brd 172.22.255.255 scope global eth1$ docker exec bridgebox wget -qO- http://dbx | grep -o '<title>.*</title>'<title>Welcome to nginx!</title>
eth0 is on frontend, eth1 on backend, and the container can reach both sides. docker network disconnect
removes it again.
Your API is on network backend. You add nginx on network frontend and point it at http://api:3000. Nginx logs 'host not found'. What's the smallest fix?
Create frontend and backend. Put an nginx on backend named secret. From a container on frontend, show you can't reach it. Then attach that container to backend as well and show you can. Clean up with docker network rm (it fails while containers are attached).
STUCK? ▸
Names don't resolve: check both containers are on the same network with docker network inspect NAME, which lists every attached container. Remember the default bridge never resolves names.
Recap
Interview questions
How do containers discover each other in Docker?
How would you stop a public-facing container from reaching your database?