Explain what docker run -p 8080:80 does: which number is which, how the capital -P flag differs, and what has to be true of the process inside the container for the mapping to work at all.
answer
- host:container, outside-in, host first
- -P = all EXPOSEd ports to random host ports
- EXPOSE = documentation only, feeds -P
- app must bind 0.0.0.0, not 127.0.0.1
- -p 127.0.0.1:8080:80 = local-only mapping
basics
~20 sThe form is host:container, so -p 8080:80 sends traffic arriving at host port 8080 to port 80 in the container. Capital -P publishes every EXPOSEd port to a random free host port; docker port shows the result. The container process must listen on 0.0.0.0, not 127.0.0.1, or nothing reaches it.
solid answer
~50 s`-p 8080:80` publishes a port: traffic arriving on **host** port 8080 is forwarded to port **80 inside the container**. The left side is always the host, the right side the container — getting this backwards is the classic beginner error. You can pin the host interface too: `-p 127.0.0.1:8080:80` binds loopback only, so the mapping is not reachable from outside the machine. `-P` (capital) publishes every port the image declares with `EXPOSE`, each to a randomly chosen high host port; `docker port <container>` or `docker ps` tells you which. It is convenient for throwaway runs and unsuitable where the port must be predictable. Two things trip people up. First, `EXPOSE` in a Dockerfile publishes nothing — it is documentation plus the input to `-P`. Second, the server inside the container must bind `0.0.0.0` (all interfaces). If it binds `127.0.0.1`, it only accepts connections on the container's own loopback, and the forwarded traffic is refused no matter how correct the mapping is.
code
bash · 6 linesdocker run -d --name web -p 8080:80 nginx:1.27
docker run -d --name admin -p 127.0.0.1:9000:9000 myadmin:1.0
docker run -d --name rand -P nginx:1.27
docker port rand 80
docker ps --format '{{.Names}}\t{{.Ports}}'go deeper
State the host:container order confidently, know -P versus -p, and know EXPOSE does not publish.
Add the host-IP form for loopback-only publishing, the 0.0.0.0 bind requirement inside the container, and that mappings are immutable after creation.
Frame publishing as exposure surface: publish only at the edge, bind admin ports to loopback, and prefer shared networks over publishing for internal traffic.
Talk about publishing policy across a fleet, predictable ports for firewalling and service registration, and why ephemeral -P suits tests but not production.
## The direction of the mapping A container on a bridge network has a private IP that is not routable from outside the host. Publishing creates a path: `-p HOSTPORT:CONTAINERPORT` tells Docker to accept connections on the host and forward them into the container. Mnemonic: read it outside-in, host first. `-p 8080:80` means "the outside world knocks on 8080, the container answers on 80". The full syntax is `-p [HOST_IP:][HOSTPORT:]CONTAINERPORT[/PROTOCOL]`: - `-p 80` — container port 80 to a random host port - `-p 8080:80` — all host interfaces (0.0.0.0) to container 80 - `-p 127.0.0.1:8080:80` — host loopback only; nothing off-box can connect - `-p 8080:80/udp` — UDP instead of the default TCP - repeat the flag for multiple ports; ranges like `-p 8000-8010:8000-8010` work too Compose's `ports:` uses the same string form, while `expose:` is the documentation-only equivalent of `EXPOSE`. ## What -P does Capital `-P` (`--publish-all`) publishes every port declared by `EXPOSE` in the image, each to an ephemeral host port drawn from the kernel's ephemeral range. It is useful when several instances of the same image must run without colliding, and in test suites that read the assigned port programmatically. `docker port web 80` prints the mapping; the ports column of `docker ps` shows the same. The obvious cost is unpredictability — the port changes on every restart, so it cannot be referenced by a fixed configuration or firewall rule. ## EXPOSE is not publishing `EXPOSE 80` in a Dockerfile opens nothing. It records intent as image metadata, which humans and tools read, and it feeds `-P`. Traffic never flows because of it. Correspondingly, you can publish a port that was never EXPOSEd; Docker does not require the declaration. ## The bind address inside the container This is the single most common cause of "my mapping does not work". Every container has its own network namespace, hence its own loopback. A server that binds `127.0.0.1:80` accepts only connections arriving on the container's own loopback interface. Forwarded traffic arrives on the container's `eth0` from the bridge, so the kernel refuses it and you see `connection refused` even though `docker ps` shows a perfect mapping. Frameworks default this way surprisingly often — development servers, some Python and Node setups, database packages. The fix is inside the app: bind `0.0.0.0` (for example `--host 0.0.0.0`, `HOST=0.0.0.0`, `listen 0.0.0.0:8080`). The symmetric point holds on the host side: `-p 127.0.0.1:8080:80` deliberately restricts a mapping to local access, which is the right default for admin interfaces and databases you only need from the machine itself. ## Related essentials Publishing is not needed for container-to-container traffic on a shared user-defined network — those containers already reach each other on all ports. Publish only what genuinely needs to be reachable from the host or beyond. A host port can hold only one binding: starting a second container with the same host port fails with "port is already allocated". Host ports below 1024 additionally need privileges the daemon normally has, which is why containers usually listen high internally and are published low externally. Finally, published ports are fixed at creation. There is no `docker update` for them; changing a mapping means recreating the container, which is one reason declarative tools like Compose are preferred over long-lived hand-run containers.
- Does EXPOSE in a Dockerfile make a port reachable from the host?No. EXPOSE only records metadata: it documents the port for readers and tooling and supplies the list that -P publishes. Reachability requires -p or -P at run time, and you can publish a port that was never EXPOSEd.
- The mapping looks right in docker ps but curl to the host port gives connection refused. What is the first thing you check?Whether the server inside the container is bound to 0.0.0.0 rather than 127.0.0.1. A loopback-only listener rejects forwarded traffic arriving on the container's eth0. Confirm with ss -ltn inside the container; the fix is in the application's bind configuration, not in the docker flags.
saying these in an interview costs you the question
- Reading -p 8080:80 as container 8080 to host 80
- Believing EXPOSE alone publishes a port
- Assuming -p is needed for containers on the same network to talk
- Ignoring that the app must listen on 0.0.0.0 inside the container
- Thinking a published mapping can be changed on a running container