Connect Containers with Docker Networks
Go beyond port publishing and learn how containers actually talk to each other. Explore the default bridge, create a user-defined network with automatic DNS, and prove container-to-container communication by name with real ping and wget tests.
Problem
You already know how to run a container and publish a port to your browser. But real applications are never one container — a web app talks to a database, a cache, a queue. The moment you have two containers that need to reach each other, a new question appears: **how does one container find and talk to another?** Beginners reach for hardcoded IP addresses, and it works — until a container restarts, gets a new IP, and everything breaks. The Docker answer is *networks*: virtual networks that connect containers and, on a user-defined network, give you **automatic DNS** so a container can reach another simply by its name, no matter what IP it lands on. In this lab you'll see exactly why the default `bridge` network isn't enough, create your own network, run two containers on it, and prove they resolve each other by name with real `ping` and `wget` output. You'll also learn to inspect networks, connect and disconnect containers on the fly, and use separate networks to isolate groups of services — the exact mechanism Compose uses under the hood.
Objectives
- Explain Docker's network model and when to use the
bridge,hostandnonedrivers - Explore the default bridge with
docker network lsanddocker network inspect, and see why containers there can only reach each other by IP - Create a user-defined bridge network with
docker network createand understand the automatic DNS it provides - Run two containers on a custom network and prove they resolve each other by name using real
ping,wgetandcurloutput from inside a container - Connect and disconnect containers from networks at runtime with
docker network connect/disconnect - Use separate networks to isolate service groups, then bridge them deliberately with a shared network — and clean everything up
Prerequisites
- Docker installed and running (Docker Desktop on Windows/macOS, or Docker Engine on Linux). Run
docker version— a Client and a Server section means you're ready. - Comfort with the basic container loop (
docker run,docker ps,docker exec,docker stop,docker rm) — this is Docker Lab 4, so the earlier labs are assumed. - A terminal (PowerShell, bash or zsh) and internet access to pull the
nginxandbusyboximages - Git installed, to submit your deliverable at the end
- No prior networking theory required — every concept is introduced when you need it
What you will build
You won't write application code in this lab. Instead you'll wire up two containers so they can
talk to each other by name — the single most important networking skill for real,
multi-service apps. You'll first watch the default bridge fail to resolve names, then create
your own network where it works, and prove it with real ping and wget output captured from
inside a container.
The one mental model: containers are isolated until a network connects them
By default a container is a sealed box. Publishing a port with -p opens a door to the host,
but it says nothing about how two containers reach each other. That's what a Docker network
is: a virtual switch that containers plug into. Two containers on the same network can talk;
two containers on different networks cannot (unless you deliberately bridge them).
The three network drivers you'll meet
| Driver | What it does | When to use it |
|---|---|---|
| bridge | The default. A private virtual network on a single host; containers get internal IPs and can talk to each other | Almost always — the standard choice for containers on one machine |
| host | Removes network isolation; the container shares the host's network stack directly (no -p needed) |
Rare — when you need maximum network performance or the container must bind host ports directly |
| none | Disables networking entirely; the container has only a loopback interface | When a container must be fully isolated from any network |
There's also overlay, used to connect containers across multiple hosts in a Swarm/cluster —
out of scope for this single-host lab, but worth knowing it exists.
The key insight: default bridge vs user-defined bridge
Both are "bridge" networks, but they behave differently, and this trips up almost everyone:
- On the default
bridgenetwork (the one containers join automatically), containers can
only reach each other by IP address — there is no automatic name resolution. - On a user-defined bridge network (one you create with
docker network create), Docker runs
an embedded DNS server so containers resolve each other by name. This is the whole
reason to create your own networks.
You'll see both behaviors firsthand. Do the steps in order — each builds on the last, and you'll
collect the outputs you need for the submission in Step 6.
Steps
-
Understand Docker's network model and drivers
Before creating anything, get the map in your head: what a Docker network is and which driver
does what.Why networks exist
A container is isolated by default. When you ran
docker run -d -p 8080:80 nginx, the-p
flag opened a path from your host to the container — but that's host-to-container. It
says nothing about how a second container would reach the first. For that, both containers
must share a network.List the networks Docker already has
A fresh Docker install ships with three networks. Look at them:
docker network lsYou'll see at least:
NETWORK ID NAME DRIVER SCOPE xxxxxxxxxxxx bridge bridge local xxxxxxxxxxxx host host local xxxxxxxxxxxx none null localThese map directly to the three drivers you'll use on a single host:
-
bridge — the default private network. Any container started without
--networkjoins
this one. Containers here get an internal IP and can reach each other by IP. -
host — no isolation; the container uses the host's network directly. Start a container
with--network hostand its ports are the host's ports (no-pmapping). Fast, but you
lose isolation and can't run two containers that want the same port. -
none — networking disabled.
--network nonegives the container only a loopback
interface. Useful for a job that must not touch the network at all.
The decision in one line
Use bridge (almost always), reach for host only when you need raw host networking,
and use none when a container must be network-isolated. For multi-host clusters there's
overlay, which this lab doesn't cover.Checkpoint
- Which driver do containers use if you don't pass
--network? (bridge — the default) - Does
-p 8080:80let two containers talk to each other? (No — it only connects the host to a container. Container-to-container needs a shared network.) - Which driver would you pick for a batch job that must never make a network call? (none)
-
bridge — the default private network. Any container started without
-
Explore the default bridge and hit its DNS limitation
Let's prove why the default bridge isn't enough, so the custom network in Step 3 feels
earned rather than arbitrary.Pull a small, ping-friendly image
You'll use busybox — a tiny image that bundles
ping,wget,nslookupand a shell,
perfect for network testing.docker pull busybox:1.36 docker pull nginx:1.27-alpineRun two containers on the default bridge
Start two long-lived busybox containers without a
--networkflag, so they join the
default bridge.sleep 3600just keeps them alive so you can exec into them.docker run -d --name c1 busybox:1.36 sleep 3600 docker run -d --name c2 busybox:1.36 sleep 3600 docker psInspect the default bridge
See who's connected and what IPs they got:
docker network inspect bridgeScroll to the
"Containers"section — you'll seec1andc2, each with anIPv4Address
like172.17.0.2/16and172.17.0.3/16. Notec2's IP; you'll use it in a second.The limitation: name resolution fails
Try to reach
c2by name from insidec1:docker exec c1 ping -c 2 c2This fails —
ping: bad address 'c2'. On the default bridge there is no DNS, so the name
c2means nothing. Now try the same container by its IP (use the address you saw in
the inspect output):docker exec c1 ping -c 2 172.17.0.3This succeeds. The containers can reach each other — they're on the same network — but
only by IP. And IPs change every time a container restarts, which makes hardcoding them a
trap.Checkpoint
- On the default bridge, why did
ping c2fail butping <c2's IP>work? (No DNS on the default bridge — names aren't resolved, only raw IPs route.) - Why is depending on the IP a bad idea? (IPs are assigned dynamically and change on restart; your config would break.)
Deliverable for this step: capture the failing
ping c2output and the succeeding
ping <IP>output — they're the "before" half of your submission. - On the default bridge, why did
-
Create a user-defined network with automatic DNS
Now the payoff. On a network you create, Docker runs an embedded DNS server and containers
resolve each other by name.Create the network
docker network create minha-redeDocker prints the new network's ID. By default it uses the bridge driver (you can be
explicit with--driver bridge, but bridge is the default for a single host). Confirm it
exists:docker network lsYou'll see
minha-redein the list alongside the built-inbridge,hostandnone.Run two containers on the new network
The
--networkflag attaches a container to a network at start time. Run an nginx web server
and a busybox client, both onminha-rede:docker run -d --network minha-rede --name web nginx:1.27-alpine docker run -d --network minha-rede --name cliente busybox:1.36 sleep 3600-
web— the nginx server, reachable on port 80 inside the network (note: no-pneeded
for container-to-container traffic; you only publish a port when the host/browser needs
in). -
cliente— a busybox box you'll run test commands from.
Inspect the custom network
docker network inspect minha-redeIn the
"Containers"block you'll see bothwebandclienteattached, each with an IP on
this network's subnet. Same as the default bridge so far — the difference is invisible until
you use a name, which is the next step.Why this matters
The instant two containers share a user-defined network, Docker registers their names in its
internal DNS.webandclientecan now find each other by name — no IPs, no config files,
no restarts breaking anything. This is precisely what Docker Compose sets up for you
automatically behind the scenes; here you're doing it by hand so you understand the machinery.Checkpoint
- What does
docker network createdefault the driver to on a single host? (bridge) - Why didn't you pass
-pwhen startingweb? (Becauseclientereaches it over the shared network on port 80 directly;-pis only for exposing a container to the host.)
Deliverable for this step: the
docker network lsoutput showingminha-rede, and the
docker network inspect minha-redeoutput showing both containers attached. -
-
Prove container-to-container communication by name
This is the heart of the lab: run real tests from inside one container against another, using
only names. This is the "after" that contrasts with Step 2's failure.Ping by name — it works now
From inside
cliente, pingwebby its name:docker exec cliente ping -c 3 webUnlike Step 2, this succeeds: you'll see replies from
web, and crucially the name
resolves to an IP onminha-rede:PING web (172.19.0.2): 56 data bytes 64 bytes from 172.19.0.2: seq=0 ttl=64 time=0.089 ms 64 bytes from 172.19.0.2: seq=1 ttl=64 time=0.101 ms ...Docker's embedded DNS turned
webinto the right IP automatically. This is the whole point
of user-defined networks.Confirm the DNS resolution explicitly
docker exec cliente nslookup webYou'll see
webresolve to its container IP via the Docker DNS server at127.0.0.11— the
embedded resolver every user-defined network gets.Fetch the actual web page by name
Ping proves reachability; now prove the service answers.
webruns nginx on port 80, so
request its homepage fromclienteusing the name:docker exec cliente wget -qO- http://webYou'll get the HTML of nginx's "Welcome to nginx!" page, retrieved over the network by name.
If your busyboxwgetoutput is quiet,wget -S http://web -O /dev/nullshows the response
headers (a200 OKfrom nginx) instead.Note on
curl: busybox shipswget, notcurl. If you prefercurl, run it from an
image that has it, e.g.docker run --rm --network minha-rede curlimages/curl:8.9.1 -s http://web.
The lesson is identical — the namewebresolves inside the network.Why this is the big deal
You just addressed another container by a stable name that survives restarts, instead of a
fragile IP. Your web app can point atdb, your worker atredis, and it keeps working no
matter which IPs Docker hands out. Name-based service discovery is the foundation every
Compose file and orchestration tool relies on.Checkpoint
- What made
ping websucceed here whenping c2failed in Step 2? (This network is user-defined, so Docker's embedded DNS resolves container names; the default bridge has no such DNS.) - Which DNS server IP did
nslookupreport? (127.0.0.11 — Docker's embedded resolver.)
Deliverable for this step: capture the successful
ping -c 3 web, thenslookup web
output, and thewget -qO- http://web(or-S) output showing nginx answered by name. - What made
-
Manage networks: connect, disconnect, isolate, diagnose
Networks aren't static. You can attach and detach running containers, and use separate
networks to isolate groups of services — the real reason networks are a security tool, not
just a convenience.Isolation: a container on a different network can't reach in
Start a third container on the default bridge (not
minha-rede) and try to reachweb:docker run -d --name estranho busybox:1.36 sleep 3600 docker exec estranho ping -c 2 webThis fails —
estranhoisn't onminha-rede, so it can't even resolve or route toweb.
Separate networks isolate services: exactly how you'd keep a database off the public-facing
network. This mirrors the ebook'swebnetvsdbnetsplit, where a web container and a db
container on different networks simply cannot talk.Connect a running container to a network
You don't have to recreate a container to network it. Attach
estranhotominha-redelive:docker network connect minha-rede estranho docker exec estranho ping -c 2 webNow it works —
estranhogained a second interface onminha-redeand can resolveweb.
This is the "shared network" trick from the ebook: two otherwise-isolated groups can be
bridged deliberately by joining a common network, without tearing anything down.Disconnect it again
docker network disconnect minha-rede estranho docker exec estranho ping -c 2 web # fails again — back to isolatedDiagnose from inside a container
When something can't connect, these tools (all in busybox) tell you why:
docker exec cliente nslookup web # is the name resolving? docker exec cliente ping -c 2 web # is the host reachable? docker exec cliente wget -S http://web -O /dev/null # is the service answering (200)? docker network inspect minha-rede # who is actually attached?Work top-down: DNS → reachability → service response → network membership. Most "it can't
connect" bugs are one container simply not being on the expected network —docker network inspectreveals it instantly.Clean up the networks
Disconnect or stop containers, then remove the network. A network can't be removed while
containers are still attached:docker rm -f web cliente c1 c2 estranho docker network rm minha-rede docker network prune # removes ALL unused networks; asks for confirmationCheckpoint
- Why couldn't
estranhoreachwebat first? (It was on the default bridge, not onminha-rede— different networks are isolated.) - How did you bridge them without recreating the container? (
docker network connect minha-rede estranhoattached it live.) - What must be true before
docker network rmsucceeds? (No containers are attached to that network.)
Deliverable for this step: capture the isolation failure, the
docker network connect+
successful ping, and the final cleanup confirming the network is gone. - Why couldn't
-
Submit: document your network and connectivity tests
Prove you wired two containers together and made them talk by name by capturing the real
evidence in a small repository.Build your submission
Create a folder, initialize git, and write a
SUBMISSION.mddocumenting what you did. Paste
the real outputs you collected — not invented text. The contrast between the default
bridge (Step 2) and your custom network (Step 4) is the core of the submission.# Docker Lab 4 — Connect Containers with Docker Networks ## 1. docker network ls (default networks + minha-rede) <paste your `docker network ls` showing bridge/host/none and minha-rede> ## 2. Default bridge: name FAILS, IP works Output of `docker exec c1 ping -c 2 c2` (should FAIL: bad address): <paste it> Output of `docker exec c1 ping -c 2 <c2's IP>` (should SUCCEED): <paste it> ## 3. docker network inspect minha-rede <paste it, showing web and cliente attached> ## 4. Custom network: name RESOLVES Output of `docker exec cliente ping -c 3 web` (should SUCCEED): <paste it> Output of `docker exec cliente nslookup web`: <paste it — note the 127.0.0.11 resolver> Output of `docker exec cliente wget -qO- http://web` (or `-S`): <paste it — nginx answered by name> ## 5. Isolation + connect/disconnect `docker exec estranho ping web` before connect (FAILS): <paste it> After `docker network connect minha-rede estranho`, ping web (SUCCEEDS): <paste it> ## 6. Clean up <paste `docker network rm minha-rede` and a final `docker network ls`> ## Reflection (2-3 sentences) In my own words: why the default bridge couldn't resolve names, what a user-defined network adds (embedded DNS), and why name-based discovery beats hardcoded IPs.Submit
git init git add SUBMISSION.md git commit -m "Docker lab 4 — container networking" # push to a public repo or create a gist, then submit that URLSubmit the repository or gist URL as your lab deliverable.
Submission criteria (self-check)
-
docker network lsoutput shows yourminha-redealongside the built-in networks - You captured the default bridge failing to resolve a name (
ping <name>→ bad address) but succeeding by IP -
docker network inspect minha-redeshows both containers attached -
ping -c 3 webfromclientesucceeds by name on the custom network -
nslookup webshows resolution via the127.0.0.11embedded DNS -
wget http://webreturned nginx's response (page HTML or a200header) — a real service answered by name - You demonstrated isolation and a live
docker network connectfixing it - A final
docker network lsconfirmsminha-redewas removed (clean host) - Your reflection correctly explains default bridge vs user-defined DNS and why names beat IPs
Anti-stub check
Every output must be real and yours. Invented IPs, copy-pasted example blocks, or a
pingthat "would work" are not a submission. The grader is looking for the contrast: the
sameping <name>that fails on the default bridge and succeeds onminha-rede. If both look
identical, you didn't actually run one of them.What's next
You can now connect containers into networks and have them discover each other by name — the
backbone of every multi-service app. But everything you've done so far is ephemeral: delete
a container and its data vanishes. In the next Docker lab you'll fix that with volumes and
data persistence — keeping databases and files alive across container restarts and rebuilds. -