Docker networking
Network drivers, user-defined bridges, port publishing, container-to-container, diagnostics.
Drivers
| Driver | What | When |
|---|---|---|
bridge (default) | private L2, NAT outbound | default for everything (user-defined recommended) |
host | shares host stack, no isolation | raw performance, listen on host port without -p (Linux only) |
none | no interfaces | sandbox, offline jobs |
overlay | multi-host L2 | Swarm only |
macvlan | own MAC on physical LAN | rare |
Default bridge
When started without --network, containers attach to the default bridge network. Containers on the default bridge can’t resolve each other by name — only by IP. Use a user-defined bridge instead.
User-defined networks
Create:
docker network create appnetWith options:
docker network create \ --driver bridge \ --subnet 172.28.0.0/16 \ --gateway 172.28.0.1 \ appnetList:
docker network lsInspect (attached containers, IPAM):
docker network inspect appnetAttach a running container:
docker network connect appnet webDetach:
docker network disconnect appnet webRemove (must have no containers):
docker network rm appnetCleanup unused:
docker network pruneUse at run:
docker run -d --name db --network appnet -e POSTGRES_PASSWORD=dev postgres:16
Inside containers on appnet, db resolves to the container’s IP via embedded DNS.
Port publishing
All host interfaces:
docker run -p 8080:80 nginxLocalhost only (not LAN-accessible):
docker run -p 127.0.0.1:8080:80 nginxSpecific interface:
docker run -p 192.168.1.10:8080:80 nginxUDP (default is TCP):
docker run -p 53:53/udp coredns/corednsRandom free host port:
docker run -p 80 nginxdocker port <container> 80# ask what it pickedPublish all
EXPOSE’d ports to random:docker run -P nginx
Warning:
-p 8080:80binds all interfaces — LAN-reachable. Bind to127.0.0.1for local-only. Docker on Linux inserts iptables rules directly and bypasses the host firewall (ufw deny 8080will NOT block a published port).
EXPOSE in Dockerfile is documentation only — still need -p at runtime. -P (capital) publishes all EXPOSE’d ports to random host ports.
Container-to-container
docker network create appnet
docker run -d \
--name db \
--network appnet \
-e POSTGRES_USER=app \
-e POSTGRES_PASSWORD=secret \
-e POSTGRES_DB=appdb \
-v pgdata:/var/lib/postgresql/data \
postgres:16
docker run -d \
--name web \
--network appnet \
-p 8080:8080 \
-e DATABASE_URL="postgres://app:secret@db:5432/appdb" \
myapp:1.0
App uses db (container name) as the hostname. DB has no -p — only the app reaches it. For >2 services, switch to Compose.
Host mode
- No NAT, no veth pair — container uses host stack directly:
docker run -d --network host nginx
-p has no effect; container listens on host ports directly. Linux only (on Mac/Windows still goes through Linux VM).
None mode
- Only
lointerface:docker run --rm --network none alpine ip addr
Diagnostics
Alpine on same network:
docker run --rm -it --network appnet alpine shping db nslookup db nc -zv db 5432Inside an existing container:
docker exec -it web shnslookup dbcurl -v http://db:5432All attached containers + IPs:
docker network inspect appnet
Quick reference
| Goal | Command |
|---|---|
| create bridge | docker network create NAME |
| list | docker network ls |
| inspect | docker network inspect NAME |
| attach at start | docker run --network NAME ... |
| attach later | docker network connect NAME CONTAINER |
| disconnect | docker network disconnect NAME CONTAINER |
| remove | docker network rm NAME |
| cleanup | docker network prune |
| publish port | -p [HOST_IP:]HOST:CONTAINER[/PROTO] |
| random host port | -p CONTAINER |
| publish all EXPOSEd | -P |
| show published | docker port CONTAINER |
| host net | --network host |
| no net | --network none |