Lesson: How Containers Talk
What you'll learn
- Describe the three core Docker network modes: bridge, host, and none.
- Publish a container port to the host with
-pand read theHOST:CONTAINERsyntax correctly. - Connect containers on a user-defined network so they reach each other by name (built-in DNS).
- Connect container networking back to the IP/ports/DNS basics from Module 1.
By the end you can wire up containers so they can be reached from outside and talk to each other reliably — the networking skill every multi-service app depends on.
The lesson
1. A quick callback to Module 1
In Module 1 you learned that a service is reachable at an IP address + port, and that DNS turns names into IP addresses. Container networking is exactly those ideas applied inside a host. Each container gets its own IP (in its own network namespace), services listen on ports, and Docker provides a little DNS server so containers can find each other by name. Keep that IP + port + DNS triangle in mind.
2. The default: bridge network
When you install Docker, it creates a virtual network called bridge (a software switch named docker0 on the host). By default every container you start joins it and gets a private IP like 172.17.0.x. Containers on the bridge can reach the outside world (the host NATs their traffic out), but the outside world cannot reach in unless you publish a port.
HOST (e.g. lab VM)
+-----------------------------------+
| docker0 bridge 172.17.0.0/16 |
| | | |
| [c1 .0.2] [c2 .0.3] |
+-----------------------------------+
| (NAT out to the network / internet)
v
List networks any time:
docker network ls
3. Publishing ports with -p
A container's port is private to its network. To let the host (and anything that can reach the host) connect, you publish it:
docker run -d --name web -p 8080:80 nginx:1.27
Read -p HOST:CONTAINER left-to-right: host port 8080 forwards to container port 80. Now a browser hitting the host on 8080 reaches nginx. You can:
-p 8080:80 # host 8080 -> container 80
-p 127.0.0.1:8080:80 # only on the host's loopback (not exposed to the network)
-p 80:80 # host 80 -> container 80
Two containers cannot both publish the same host port — that port is a single host resource. Choose distinct host ports.
4. host and none modes
Besides bridge, two other modes are worth knowing:
- host (
--network host): the container skips its own network namespace and shares the host's network directly. No-pneeded — a container listening on 80 is on the host's 80. Faster, but no isolation and easy port clashes. Use sparingly. - none (
--network none): the container gets no networking at all. Useful for a job that should be fully offline (e.g. processing a local file).
docker run --rm --network host nginx:1.27 # shares host net
docker run --rm --network none alpine ip a # only loopback exists
5. The key skill: user-defined networks + DNS
The default bridge does not give containers name-based discovery. The professional pattern is to create your own user-defined bridge network. On a user-defined network, Docker runs an embedded DNS server: every container is reachable by its container name as a hostname. No hard-coded IPs.
# 1. Create a network
docker network create appnet
# 2. Start a database on it
docker run -d --name db --network appnet \
-e POSTGRES_PASSWORD=<REDACTED> postgres:16
# 3. Start an app on the SAME network
docker run -d --name api --network appnet -p 8080:8080 myorg/api:sha-abc123
Now inside the api container, the database is reachable simply as the hostname db on port 5432 — Docker's DNS resolves db to the database container's current IP automatically.
appnet (user-defined bridge, with DNS)
+-----------------------------------------------+
| [api] --- resolves "db" ---> [db] |
| :8080 :5432 |
+-----------------------------------------------+
|
-p 8080:8080 (only the api is published to the host)
This is why your app config should say db:5432, not 172.17.0.3:5432 — IPs change when containers restart, names do not. Compose (Chapter 4) automates exactly this: it creates a network and puts every service on it so they reach each other by service name.
6. Inspecting and troubleshooting
When something cannot connect, inspect the wiring:
# Which network is a container on, and what is its IP?
docker inspect web --format '{{json .NetworkSettings.Networks}}'
# Who is attached to a network?
docker network inspect appnet
# Test DNS / reachability FROM inside a container
docker exec -it api sh
# then inside: ping db / wget -qO- http://db:5432 (or curl)
Common gotchas:
- Trying to reach a container by name on the default bridge — DNS only works on user-defined networks. Fix: create and use your own network.
- Container "works" but is unreachable from your laptop — you forgot
-p, or published to127.0.0.1only. - Two containers fighting over the same host port — change one
-phost side.
7. Mental model to keep
- Inside a network, containers talk to each other by name on the container port (e.g.
db:5432). - From outside, you reach a container only through a published host port (
-p). Keep those two paths separate in your head and most networking confusion disappears.
Dig deeper
- Docker networking overview
- Bridge network driver & user-defined bridges
- Container networking & DNS
- Publishing ports (
-p) - Podman networking
Search terms
docker bridge vs host vs none networkdocker -p port publishing explaineddocker user-defined network container dns by namedocker network create connect containersdocker container cannot reach other container by name
Check yourself
- What does
-p 8080:80mean — which side is the host, which is the container? - Why can't containers on the default bridge resolve each other by name, and what fixes that?
- On a user-defined network named
appnet, how does theapicontainer reach adbcontainer listening on 5432? - What does
--network hostchange, and what is the downside? - Why should an app's config reference
db:5432instead of a hard-coded IP like172.17.0.3:5432?
No comments to display
No comments to display