07 - Networks and Ports¶
What this session is¶
About 45 minutes. How containers talk to your host, to each other, and to the internet. The four network types Docker creates, why containers can find each other by name, and how to debug "why can't service A reach service B?"
The mental model¶
Each container gets its own network namespace - its own IP address, its own ports, its own loopback. Two consequences:
- The container's
localhostis not your host'slocalhost. They're separate worlds. - To reach a container's port from outside, you have to publish it with
-p(host port forwards to container port).
Networks tie containers together. Containers on the same Docker network can see each other; containers on different networks can't.
The default networks¶
You'll see at least:
| Network | Driver | What it's for |
|---|---|---|
bridge |
bridge | Default for containers without --network |
host |
host | Container shares the host's network (no isolation) |
none |
null | No networking at all |
The default bridge (bridge) is what containers join automatically. Containers on the default bridge get IPs but cannot find each other by name (a quirky default).
Create a user-defined network¶
Now run containers on it:
docker run -d --name db --network mynet -e POSTGRES_PASSWORD=secret postgres:16
docker run -d --name web --network mynet -p 8080:80 nginx
Both db and web are on mynet. They can reach each other by container name:
This is the right pattern for multi-container apps. Always create a user-defined network. Don't rely on the default bridge.
Port publishing: -p HOST:CONTAINER¶
A container on a Docker network is reachable from other containers on the same network. To reach it from your host (or from outside your machine), you must publish the port:
Now http://localhost:8080 on your host hits the nginx in the container.
Options:
docker run -p 8080:80 nginx # any interface on host port 8080
docker run -p 127.0.0.1:8080:80 nginx # only loopback on host
docker run -p 8443:443 -p 8080:80 nginx # publish multiple ports
docker run -P nginx # publish all EXPOSE'd ports to random host ports
docker port web shows the actual host port mapping.
Two containers talking¶
A canonical pattern: app and database.
docker network create mynet
docker run -d --name db --network mynet \
-e POSTGRES_PASSWORD=secret \
-v pgdata:/var/lib/postgresql/data \
postgres:16
docker run -d --name app --network mynet -p 8080:8080 \
-e DATABASE_URL=postgres://postgres:secret@db:5432/postgres \
my-app:1.0
Inside the app container, db resolves to the database container's IP. Port 5432 is reachable from app (containers on the same network can reach any port without explicit publishing). Outside the cluster, only port 8080 (on the host) is exposed.
host networking (Linux only)¶
nginx is on the host's network - port 80 binds directly to host port 80. No port mapping needed. No isolation; the container can see and use any host network interface.
Useful for performance-sensitive networking (lower overhead than the bridge). Not available on macOS/Windows because Docker runs in a VM there.
Inspect a network¶
Shows all containers attached, their IPs, the subnet, etc.
docker network inspect mynet --format='{{range .Containers}}{{.Name}} {{.IPv4Address}}{{"\n"}}{{end}}'
Useful for "what IP did Docker give my container?"
Disconnect, reconnect¶
Useful for testing failure scenarios (disconnect the database, see how the app behaves).
DNS inside containers¶
Docker runs an embedded DNS resolver. Inside a container on a user-defined network:
The 127.0.0.11 is Docker's embedded DNS. It resolves container names + external hostnames.
Test:
docker exec -it app nslookup db
# Should return the db container's IP
docker exec -it app nslookup google.com
# Should return Google's IP
If DNS resolution fails, networking is broken in some way. Common cause: container is on the default bridge (no name resolution). Recreate on a user-defined network.
Debugging "service A can't reach service B"¶
When connectivity isn't working:
-
Are they on the same network?
Both should list the same network. -
Can A's container resolve B's name?
If "no such host," they're not on the same network OR A is on the default bridge. -
Can A reach B's port?
Success: port is open. "Connection refused": B isn't listening yet (race) or B's app crashed. -
Is B's app actually listening?
Some images don't includenetstatorss; install or skip. -
Read B's logs:
Did it start cleanly? Is it bound to0.0.0.0(all interfaces) and not127.0.0.1(only its own loopback)?
The single most-common bug: an app inside a container binding to 127.0.0.1 instead of 0.0.0.0. Only the container's own loopback can reach it. The fix: configure the app to bind to all interfaces.
Exercise¶
-
Create a network and ping by name:
-
Two-container app:
-
Port publishing experiment:
-
Default bridge gotcha:
What you might wonder¶
"Can I make a container available outside my machine?"
Yes - -p 80:80 binds to all interfaces by default, including external ones. Anyone on your network can reach http://your-machine-ip:80. Firewall accordingly. To bind only to localhost: -p 127.0.0.1:80:80.
"What about IPv6?" Possible but disabled by default. Enable in Docker daemon config. Beyond beginner scope.
"Why is the default bridge so awkward?" Historical reasons. The default bridge was the original; user-defined networks were added later with better defaults. The original is kept for backwards compatibility but isn't recommended for new use.
"What about overlay networks?" For multi-host setups (containers on different machines, like in Docker Swarm or Kubernetes). Beyond beginner scope; recognize the name.
Done¶
- Understand container network isolation.
- Create user-defined networks for multi-container apps.
- Use container names for service discovery.
- Publish ports with
-p. - Debug network issues (DNS, port binding, connection refused).