Chapter 50 — Container networking: how containers talk to the world
In one sentence
By default, Docker creates a private, virtual network for your containers to talk to EACH OTHER by name, and you must explicitly publish a port to make a container reachable from OUTSIDE that network — both mechanisms are Chapter 37's networking concepts, applied inside a container's own network namespace.
Why this matters
"Why can't I reach my container from my browser" and "why can my two containers talk to each other by name" are both answered by understanding exactly what Docker's networking layer is doing.
How it actually works
docker network ls # Docker creates a default
# "bridge" network; you can
# also make your own
docker run -p 8080:80 nginx # PUBLISH: map the HOST's
# port 8080 to the
# CONTAINER's port 80 --
# WITHOUT this flag, the
# container's port is only
# reachable from OTHER
# containers on the same
# Docker network, never from
# the host or outside world
docker network create mynet # a custom, USER-DEFINED
# network
docker run --network mynet --name web nginx
docker run --network mynet --name db postgres
# `web` can now reach `db` simply as "db" -- Docker runs a built-in
# DNS resolver on user-defined networks, resolving CONTAINER NAMES
# to their internal IP addresses automatically (Chapter 37's DNS
# concept, provided for you)
THE NETWORK DRIVERS, briefly:
bridge (the default) an isolated virtual network on this host;
containers on it can reach each other; the
outside world can reach IN only via
published ports.
host the container uses the HOST's network
namespace DIRECTLY, no isolation at all --
faster, but loses the isolation Chapter 44
described.
none no networking at all.
overlay spans MULTIPLE HOSTS -- the foundation
for Docker Swarm, and conceptually a
preview of what Kubernetes' networking
model (Part 16) does at a larger scale.
Real-world example
The reason Docker Compose (Chapter 51) creates a DEDICATED,
PROJECT-SPECIFIC network automatically, rather than putting
everything on the shared default "bridge" network, is directly this
chapter's DNS-by-container-name mechanism, made practical: Compose's
own official documentation describes exactly this behaviour -- every
service defined in a `docker-compose.yml` file can reach every OTHER
service simply by its SERVICE NAME (e.g. an application container
connecting to `postgresql://db:5432/...`, where `db` is just the
name given to the database service in the same compose file), with
NO manual IP address configuration, NO manual network creation, and
NO hardcoded addresses anywhere -- because Compose automatically
creates a user-defined network (this chapter's exact mechanism) for
the whole project and gives every service that network's built-in
name resolution for free.
This is a genuinely practical, extremely common real pattern: almost
every real multi-container local-development setup you'll encounter
relies on this exact "reach the other service by its plain name"
mechanism, and it stops feeling like magic the moment you know it's
just Chapter 37's DNS resolution, running on a Docker-managed virtual
network, automatically configured for you.
Try it yourself
# See the difference publishing makes
docker run -d --name unpub nginx
curl -s localhost:80 > /dev/null 2>&1 && echo "reachable" || echo "NOT reachable (expected -- no -p flag)"
docker rm -f unpub
docker run -d --name pub -p 8080:80 nginx
sleep 1
curl -s localhost:8080 > /dev/null 2>&1 && echo "reachable now!" || echo "still not reachable -- check Docker is running"
docker rm -f pub
# See container-name DNS resolution on a user-defined network
docker network create demonet
docker run -d --name pingtarget --network demonet alpine sleep 300
docker run --rm --network demonet alpine ping -c 2 pingtarget # reaches
# it BY
# NAME
docker rm -f pingtarget
docker network rm demonet
Common mistakes
- Forgetting
-pand assuming a container should just be reachable. Without publishing, it's isolated on Docker's internal network — invisible from the host or outside world by design. - Confusing
-p 8080:80order. It's alwaysHOST:CONTAINER— a common, easy-to-transpose mistake. - Putting everything on the default "bridge" network and wondering why name-based resolution doesn't work. Container-name DNS resolution only works on USER-DEFINED networks — create one (or let Compose do it for you).
Check yourself
- What does
-p 8080:80actually do, and what's reachable without it? - Why can containers on a user-defined network reach each other by name, with no manual configuration?
- Why does the default "bridge" network NOT support this name-based resolution?
Further reading
- Docs: Docker's own official "Networking overview" and "Networking with standalone containers" documentation.
- Reference:
docker network --help. - Guide: this wiki's
networking/tcp-ipguide covers DNS resolution itself in far greater depth than this chapter's Docker-specific summary.