Nothing inside a container is reachable from your browser until you publish its port.
Containers get their own network namespace with a private IP. A server listening on port 80 inside a container is not reachable from the host or the outside world until you publish the port:
docker run -d -p 8080:80 nginx:1.27The format is -p [host_ip:]host_port:container_port[/protocol]. Here, host port 8080 forwards to container port 80, so http://localhost:8080 reaches nginx.
Details that matter:
- By default the port is bound on all host interfaces (
0.0.0.0and::), so it's reachable from your network, not just localhost. On Linux, Docker's own iptables/nftables rules mean it can bypass host firewalls such as ufw. For anything local-only, use-p 127.0.0.1:8080:80. - Two containers can't publish the same host port. Pick different host ports (
8081:80,8082:80) or put a reverse proxy in front. -p 80(no host port) picks a random free host port;docker port webshows the mapping.-Ppublishes every port the imageEXPOSEs to random host ports.EXPOSEalone publishes nothing.- UDP needs
/udp:-p 5353:53/udp.
The app inside must listen on 0.0.0.0, not 127.0.0.1. A dev server bound to the container's own loopback is unreachable through a published port. Many frameworks default to localhost, so this is the number one "port is published but connection refused" cause.
Containers talking to each other don't need published ports at all; they use a shared network, covered next.
Example
docker run -d --name web -p 8080:80 nginx:1.27
curl -s localhost:8080 | head -1 # <!DOCTYPE html>
docker run -d --name admin -p 127.0.0.1:9000:80 nginx:1.27 # local only
docker run -d --name rand -p 80 nginx:1.27
docker port rand # 80/tcp -> 0.0.0.0:49154
# App must listen on 0.0.0.0 inside the container
docker run --rm -p 8000:8000 python:3.13 \
python -m http.server 8000 --bind 0.0.0.0Remember
- -p host:container publishes a port; EXPOSE is documentation only
- Default binding is all interfaces; use 127.0.0.1: for local-only
- The app must listen on 0.0.0.0 inside the container
- Container-to-container traffic doesn't need published ports
Watch out for
- Publishing a database with -p 5432:5432 on a cloud VM and exposing it to the internet despite the firewall.