CertGrid CertGrid
Hands-on Lab·Docker

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

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.

One Docker host is all this guide needs. Nothing here depends on a second machine, and the hardware above is modest on purpose - a 2 core, 4 GB VM runs everything in this path.
Server NameIP AddressOSRolesCPURAMHDD
DOCKER01192.168.0.21Ubuntu 26.04 LTSDocker Host2 Core4 GB50 GB

Before you start

  1. 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:CONTAINER creates 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 -> [::]:8080

    Expected resultA container ID, then the mapping on both IPv4 and IPv6.

    Success conditiondocker port reports a mapping. If it prints nothing, no port was published and the container is only reachable from other containers.

  2. Verify from the host

    Prove the mapping rather than assuming it. 0.0.0.0 means the port is bound on every host interface - on an internet-facing machine that is a public exposure, so bind to 127.0.0.1:8080:80 when the service is only for local use.

    bash
    curl -s -o /dev/null -w "HTTP %{http_code}\n" http://localhost:8080HTTP 200

    Expected resultHTTP 200.

    Success conditionHTTP 200. HTTP 000 with exit 7 means nothing answered - either the container is not ready yet, or nothing is listening on the container port you mapped.

  3. Two containers on the default bridge cannot resolve each other

    This is the single most surprising behaviour in Docker networking. Containers started without a --network flag 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 ms

    Expected 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 - check docker inspect --format "{{.HostConfig.NetworkMode}}".

  4. 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 ms

    Expected resultThe same ping that failed now resolves cg-b and succeeds.

    Success conditionThe name resolves. In practice you would start containers with --network cg-net rather than connecting them afterwards.

  5. 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/16

    Expected 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.

  6. 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-net

    Expected resultThe container names echoed, then the network name.

    Success conditiondocker network ls no longer lists cg-net.

Troubleshooting

Official sources