Docker Port Publishing and Container Networking
Why -p 8080:80 works, why two containers on the default bridge cannot find each other by name, and why creating your own network fixes it. The failure is shown as it actually happens, not described.
Storage and Networking Guide 9 of 46 Beginner
- OSUbuntu 26.04 LTS (resolute)
- Docker Engine29.7.2
- Shellbash
- Architectureamd64
- TimeAbout 14 min
- Reviewed20 August 2026
Tested on the versions above. Container IP addresses, network IDs and subnets are assigned per host and per network. Match the shape of the output, not the exact strings.
| Server Name | IP Address | OS | Roles | CPU | RAM | HDD |
|---|---|---|---|---|---|---|
| DOCKER01 | 192.168.0.21 | Ubuntu 26.04 LTS | Docker Host | 2 Core | 4 GB | 50 GB |
Before you start
- Docker Engine running and the ability to run containers - guides 3 and 4 in this path.
- Host port 8080 free.
curlavailable on the host for the verification steps.
-
Publishing a port maps host to container
A container gets its own network namespace, so a service listening on port 80 inside is not reachable from the host by default.
-p HOST:CONTAINERcreates the mapping. Read it left to right as "host 8080 forwards to container 80" - getting the order backwards is the most common mistake.bash Example session docker run -d --name cg-web -p 8080:80 nginx:alpinee7cca8cbc07d53df511ddca22b6a143f300148e6937875be0ccae52f429f580cdocker port cg-web80/tcp -> 0.0.0.0:808080/tcp -> [::]:8080Expected resultA container ID, then the mapping on both IPv4 and IPv6.
Success condition
docker portreports a mapping. If it prints nothing, no port was published and the container is only reachable from other containers. -
Verify from the host
Prove the mapping rather than assuming it.
0.0.0.0means the port is bound on every host interface - on an internet-facing machine that is a public exposure, so bind to127.0.0.1:8080:80when the service is only for local use.bash curl -s -o /dev/null -w "HTTP %{http_code}\n" http://localhost:8080HTTP 200Expected resultHTTP 200.
Success conditionHTTP 200.
HTTP 000with exit 7 means nothing answered - either the container is not ready yet, or nothing is listening on the container port you mapped. -
Two containers on the default bridge cannot resolve each other
This is the single most surprising behaviour in Docker networking. Containers started without a
--networkflag join the default bridge, which deliberately has no DNS. They can reach each other by IP, but not by name. Run it and see the failure.bash Example session docker run -d --name cg-a alpine sleep 300docker run -d --name cg-b alpine sleep 300docker exec cg-a ping -c1 -W2 cg-bping: bad address 'cg-b'# exit code 1 - the name did not resolvedocker exec cg-a ping -c1 -W2 172.17.0.364 bytes from 172.17.0.3: seq=0 ttl=64 time=0.092 msExpected resultThe name lookup fails, the IP works. Connectivity is fine - only name resolution is missing.
Success conditionYou reproduced
bad address. If the name resolved, the containers are not on the default bridge - checkdocker inspect --format "{{.HostConfig.NetworkMode}}". -
A user-defined network gives you DNS
Networks you create yourself include an embedded DNS server, so containers on them resolve each other by container name. This is why every piece of real-world advice says to create a network rather than relying on the default bridge. Note the subnet differs from the default bridge's 172.17.
bash Example session docker network create cg-net11b463650c8bbea5318ae45141274248f1e79716273e5b7a6b5bb2accfc4db8edocker network connect cg-net cg-a && docker network connect cg-net cg-bdocker exec cg-a ping -c1 -W2 cg-b64 bytes from 172.19.0.3: seq=0 ttl=64 time=0.596 msExpected resultThe same ping that failed now resolves
cg-band succeeds.Success conditionThe name resolves. In practice you would start containers with
--network cg-netrather than connecting them afterwards. -
See who is on the network
Inspecting the network shows its driver, its subnet and every attached container with the address it was given. This is the fastest way to confirm two services are genuinely on the same network when they cannot reach each other.
bash Example session docker network inspect cg-net --format "{{.Name}} driver={{.Driver}} subnet={{(index .IPAM.Config 0).Subnet}}"cg-net driver=bridge subnet=172.19.0.0/16docker network inspect cg-net --format "{{range .Containers}}{{.Name}}={{.IPv4Address}} {{end}}"cg-a=172.19.0.2/16 cg-b=172.19.0.3/16Expected resultThe driver and subnet, then each attached container and its address.
Success conditionBoth containers are listed. A container missing here is on a different network, which is why it cannot be reached.
-
Clean up
Remove the containers before the network - Docker refuses to delete a network that still has endpoints attached.
bash # removal is permanent; these names are from this guide onlydocker rm -f cg-a cg-b cg-webdocker network rm cg-netcg-netExpected resultThe container names echoed, then the network name.
Success condition
docker network lsno longer lists cg-net.
Troubleshooting
Bind for 0.0.0.0:8080 failed: port is already allocated
Why: Another process or container already holds that host port.
Fix:Find the holder, or publish on a different host port. Only the host side needs to change.
bash docker ps --filter publish=8080 --format "{{.Names}} {{.Ports}}"docker run -d --name cg-web2 -p 8081:80 nginx:alpineOne container cannot reach another by name
Why: Either they are on the default bridge, which has no DNS, or they are on different user-defined networks.
Fix:Put both on the same user-defined network. Confirm with network inspect before changing anything else.
bash docker inspect cg-a --format "{{range $k, $v := .NetworkSettings.Networks}}{{$k}} {{end}}"# if this prints only "bridge", that is the problemThe service works from the host but not from another machine
Why: The port was published to the loopback interface only, or a host firewall is blocking it.
Fix:Check the bind address in
docker port.127.0.0.1:8080is local-only by design;0.0.0.0:8080is reachable externally if the firewall allows it.bash docker port cg-webss -ltn | grep 8080