DevOps Peach

Docker & Kubernetes · Docker Networking · Lesson 2 of 4

Bridge networks and port mapping

Containers on a bridge have private IPs; publish ports with -p so the host and the outside world can reach them.

Practice interactively →

A bridge is a private network on the host

Each bridge network is a virtual switch on the Docker host. Containers on the same bridge can talk to each other; containers on another bridge cannot see them. An IP such as 172.17.0.2 only means something inside the host; other machines on the LAN cannot reach it directly.

flowchart LR
  C["Client"] -- "203.0.113.10:8080" --> NAT["NAT -p 8080:80"]
  subgraph HOST["Docker host"]
    NAT --> W["web 172.18.0.2:80 (app-net)"]
    D["db 172.19.0.2:5432 (db-net)"]
  end

Publishing ports

Syntax Result
-p 8080:80 Every host interface, port 8080 → container 80
-p 127.0.0.1:8080:80 Only the host itself (localhost) can connect
-p 80 Docker picks a random host port
docker port web Show the current mappings of container web

Two containers cannot publish the same host port: the second one fails with port is already allocated. Change the host port (-p 8081:80) and keep the container port.

Default bridge vs user-defined bridge

Default bridge User-defined bridge
Reach others by container name No (IP only, or legacy --link) Yes, automatic DNS
Isolation Every container without a network shares it Only containers you attach
Attach/detach while running Limited docker network connect/disconnect
Own configuration Shared for the whole host Subnet and options per network

So for a multi-container app, always create a user-defined bridge instead of using the default bridge.

How publishing works underneath

On Linux, Docker implements -p with NAT rules in iptables (or nftables): traffic arriving on the host port is rewritten to the container's IP and port, and outgoing traffic from containers is masqueraded behind the host's address. sudo iptables -t nat -L DOCKER -n shows the rules Docker created. -P (--publish-all) publishes every port listed by EXPOSE to random host ports.

The firewall trap

Because Docker inserts its own rules before most host firewall rules, a published port can be reachable from the internet even when ufw appears to block it. If a service should only be local, publish it on 127.0.0.1 (-p 127.0.0.1:5432:5432), or add rules to the DOCKER-USER chain, which Docker leaves for administrators. In the cloud, also check the VM's security group.

The app must listen on all interfaces

A frequent mistake is an application that listens on 127.0.0.1 inside the container. The port mapping then forwards traffic to the container's network interface, where nothing is listening, and the client gets "connection reset". Configure the app to listen on 0.0.0.0 (for example flask run --host=0.0.0.0).

Checking connectivity

Step Command
Is the container running and the port mapped? docker ps, docker port web
Is the app listening inside? docker exec web ss -lnt (if the image has ss)
Can the host reach it? curl -I http://localhost:8080
What network and IP does it have? docker inspect -f '{{json .NetworkSettings.Networks}}' web

IPv6 and outbound traffic

Bridges are IPv4 by default; IPv6 can be enabled per network with --ipv6. Outbound connections from containers work without any -p: publishing is only needed for traffic coming into the container from outside the host.

Practice interactively →