Learn how Docker networking works: network drivers, the default bridge, container IP addresses, internet access, published ports and reaching the host.

Bishal Biswas
Self Learner
Why containers need networking
A real app is rarely one container. The notes-api you built earlier needs to talk to a database. A frontend needs to talk to an API. And all of them may need to reach the internet to download packages or call other services.
So you need to answer three questions:
- How does a container reach the internet?
- How do you (and your users) reach a container?
- How do containers reach each other?
Docker answers all three with networks. This lesson gives you the big picture and shows how the default network behaves. The next two lessons go deeper into the network types you will actually use.
You do not need to be a network expert. If words like “IP address” and “port” are new to you, read IP Addresses, Ports and Protocols first. It takes ten minutes.
Every container gets its own network
When Docker starts a container, it gives the container its own private network namespace. A namespace is a Linux feature that makes a process think it has something all to itself. With a network namespace, the container gets:
- its own network card (a virtual one, called
eth0), - its own IP address,
- its own ports, so two containers can both listen on port 80 without a fight,
- its own loopback address,
127.0.0.1, which means “this container only”.
Think of each container as a small flat in a big building. Each flat has its own door number (IP address) and its own rooms (ports). The building’s hallway is the network that connects the flats.
You can see this for yourself:
docker run --rm alpine:3.22 ip addr show eth0
2: eth0@if7: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
The container has the address 172.17.0.2. That address is private: it only means something on this Docker host.
Network drivers
Docker can build different kinds of networks. Each kind is made by a driver, a plug-in that knows how to wire containers together in one particular way.
| Driver | What it does | Where you learn more |
|---|---|---|
bridge |
A private network on one host. Containers on it can talk to each other. The default. | Bridge Networks and Container DNS |
host |
No separate network. The container uses the host’s network directly. | Host and None Networks, and When to Use Them |
none |
No network at all, only loopback. | Host and None Networks, and When to Use Them |
overlay |
One network across many hosts, used by Docker Swarm. | Beyond this course |
macvlan / ipvlan |
Gives a container an address on your real local network, like a physical machine. | Beyond this course |
For everyday work you will use bridge networks almost all the time.
The networks Docker creates for you
Docker creates three networks when it is installed. List them:
docker network ls
NETWORK ID NAME DRIVER SCOPE
3f9c2a1b7d10 bridge bridge local
8e1d44c0a2b9 host host local
c0a7f5e9d321 none null local
bridgeis the default bridge network. Every container joins it unless you say otherwise.hostandnoneare special networks for thehostandnonedrivers.SCOPE localmeans the network exists only on this machine.
The default bridge network
A bridge network is like a small network switch inside your computer. Containers plug into it, get an address from it, and can send traffic to each other through it. On Linux you can even see the bridge as a network card on the host called docker0.
the host (your computer or server)
+-------------------------------------------------------------+
| |
| container web1 container web2 |
| 172.17.0.2 172.17.0.3 |
| \ / |
| \ / |
| [ bridge network "bridge" (docker0) 172.17.0.1 ] |
| | |
| host network card ----> internet |
+-------------------------------------------------------------+
Containers can reach each other by IP address
Start two containers and look at their addresses:
docker run -d --name web1 nginx:1.29-alpine
docker run -d --name web2 nginx:1.29-alpine
docker inspect --format '{{ .NetworkSettings.IPAddress }}' web1 web2
172.17.0.2
172.17.0.3
From web2, try to reach web1 by its IP address:
docker exec web2 wget -qO- http://172.17.0.2
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
It works. Both containers are on the same bridge, so they can talk.
But not by name
Now try the container’s name instead:
docker exec web2 wget -qO- http://web1
wget: bad address 'web1'
This is the most important fact about the default bridge network: it has no name-based DNS. DNS is the system that turns names into IP addresses (you can read how it works in How DNS Works). On the default bridge, containers cannot find each other by name.
Why is that a problem? Container IP addresses change. Stop and start web1 in a different order and it may get 172.17.0.3 next time. Writing IP addresses into your app’s settings would break all the time.
The fix is a user-defined bridge network, which you create yourself. It has built-in DNS, so containers find each other by name. That is the topic of Bridge Networks and Container DNS.
Note: Old tutorials use
--linkto connect containers on the default bridge. It is a legacy feature. Use a user-defined network instead.
Question 1: reaching the internet
Containers on a bridge network can reach the internet out of the box:
docker run --rm alpine:3.22 wget -qO- https://example.com | head -n 4
<!doctype html>
<html>
<head>
<title>Example Domain</title>
This works through NAT (network address translation). The container sends traffic to the bridge, and the host forwards it to the internet as if it came from the host itself. Replies come back the same way. The outside world never sees the container’s private 172.17.x.x address.
Docker also gives each container DNS settings, usually copied from the host, so names like example.com work.
Question 2: reaching a container from outside
Traffic can go out freely, but nothing from outside can come in unless you open a door. That door is a published port, which you learned about in Publishing Ports: Reaching Apps Inside Containers:
docker run -d --name web -p 8080:80 nginx:1.29-alpine
Now port 8080 on the host forwards to port 80 inside the container.
browser --> host port 8080 --> bridge --> container web, port 80
By default -p 8080:80 listens on all of the host’s network addresses. On a server, that means anyone on the internet who can reach the server can reach the container. To allow only your own machine, bind to 127.0.0.1:
docker run -d --name admin -p 127.0.0.1:8081:80 nginx:1.29-alpine
Warning: On Linux, Docker adds its own firewall rules for published ports. Those rules can skip past simple firewall tools such as
ufw, so a port you thought was blocked may still be open. If a port should be private, bind it to127.0.0.1or do not publish it at all.
Check which ports a container publishes:
docker port web
80/tcp -> 0.0.0.0:8080
80/tcp -> [::]:8080
Question 3: reaching the host from a container
Sometimes a container needs to talk to a program running directly on your computer, for example a database you installed without Docker. Inside a container, localhost means the container itself, not your computer, so localhost will not work.
Docker Desktop gives you a special name for this: host.docker.internal.
# On Docker Desktop: reach a server running on your computer's port 5000
docker run --rm alpine:3.22 wget -qO- http://host.docker.internal:5000
On Linux with Docker Engine, that name does not exist by default. Add it yourself with the special value host-gateway:
docker run --rm --add-host=host.docker.internal:host-gateway \
alpine:3.22 wget -qO- http://host.docker.internal:5000
Tip: The program on your computer must listen on an address the container can reach. If it only listens on
127.0.0.1, it may refuse connections from containers. This is the same rule as inside containers: servers should listen on0.0.0.0.
Looking at a network
docker network inspect shows everything about a network, including which containers are on it:
docker network inspect bridge --format '{{ json .Containers }}'
{"5b1c...":{"Name":"web1","IPv4Address":"172.17.0.2/16"},"9d2e...":{"Name":"web2","IPv4Address":"172.17.0.3/16"}}
(The real output has a few more fields per container. They are trimmed here.)
Common mistakes
Using localhost to reach another container
Inside a container, localhost is that container only. To reach another container, use its name on a user-defined network, or a published port from the host.
Expecting names to work on the default bridge
They do not. Create your own network with docker network create.
Publishing database ports to the world
If only other containers need the database, do not publish its port. Containers on the same network can reach it without -p.
App listening on 127.0.0.1 inside the container
Published traffic arrives through eth0, not loopback. Make your app listen on 0.0.0.0. The notes-api already does this.
Quick recap
- Each container has its own network namespace: its own IP address, ports and
localhost. - Drivers decide how a network works:
bridge(default),host,none,overlay,macvlan. - Docker creates three networks:
bridge,hostandnone. - On the default bridge, containers reach each other by IP but not by name.
- Containers reach the internet through NAT; outsiders reach containers only through published ports.
- Bind sensitive ports to
127.0.0.1. Usehost.docker.internalto reach the host (add--add-host=host.docker.internal:host-gatewayon Linux).
Next, you will create your own networks so containers can find each other by name in Bridge Networks and Container DNS.
# Docker networking basics cheat sheet
# ---------- See the networks Docker created ----------
docker network ls
# ---------- Every container has its own IP on the default bridge ----------
docker run -d --name web1 nginx:1.29-alpine
docker run -d --name web2 nginx:1.29-alpine
docker inspect --format '{{ .NetworkSettings.IPAddress }}' web1 web2
# By IP: works
docker exec web2 wget -qO- http://172.17.0.2
# By name: fails on the default bridge (no DNS there)
docker exec web2 wget -qO- http://web1
# ---------- Containers can reach the internet through NAT ----------
docker run --rm alpine:3.22 wget -qO- https://example.com
# ---------- Let the outside world in with a published port ----------
docker run -d --name web -p 8080:80 nginx:1.29-alpine
# Only this machine can reach it
docker run -d --name admin -p 127.0.0.1:8081:80 nginx:1.29-alpine
docker port web
# ---------- Reach a program running on the host ----------
# Docker Desktop:
docker run --rm alpine:3.22 wget -qO- http://host.docker.internal:5000
# Linux Docker Engine: add the name yourself
docker run --rm --add-host=host.docker.internal:host-gateway alpine:3.22 wget -qO- http://host.docker.internal:5000
# ---------- Look inside a network ----------
docker network inspect bridge
# ---------- Clean up ----------
docker rm -f web1 web2 web admin
Was this explanation helpful?
Your feedback helps us improve the content for everyone.
Thank you for your feedback!
We appreciate you helping us improve.
What could we improve? (Optional)
Read aloud
Ready