Managing ports

Publishing, interfaces, ranges, and exposure.

Intermediate10 min · lesson 13 of 30

Publishing is the one bridge between Docker’s private networks and the outside world, so it deserves precision. -p HOST:CONTAINER maps a host port to a container port using NAT; the container port is where the app listens, the host port is what the world dials. EXPOSE in a Dockerfile only documents intent — it publishes nothing. And -P (capital) auto-publishes every EXPOSE-d port to a random high host port.

terminal
$ docker run -d -p 8080:80 nginx:1.27 # host 8080 -> container 80
$ docker run -d -P nginx:1.27 # EXPOSEd ports -> random host ports
$ docker port <id>
80/tcp -> 0.0.0.0:49154 # see what -P chose

Bind to an interface, choose a protocol

The full form is -p [HOST_IP:]HOST:CONTAINER[/proto]. Pin the host IP to control exposure: 127.0.0.1:8080:80 makes the service reachable only from the host itself, while a bare 8080:80 binds 0.0.0.0 — every interface, including public ones on a cloud VM. Append /udp for UDP services (DNS, syslog); the default is TCP. You can publish the same container port as both TCP and UDP with two -p flags.

terminal
$ docker run -d \
-p 127.0.0.1:9090:9090 \ # host-only metrics port
-p 53:53/udp \ # a UDP service
-p 8080:80 \ # public HTTP
myimg

Publishing a range, and the internal case

You can map a contiguous range (-p 8000-8010:8000-8010) for services that use several ports, and you can map to a different host port than the container port to avoid collisions when running several copies. The most important rule, though, is the negative one: containers on the same user-defined network reach each other with no publishing at all, so publish only what genuinely must be reached from outside the host.

terminal
# two copies of the same image, no collision, both host-only
$ docker run -d -p 127.0.0.1:8081:80 --name a nginx:1.27
$ docker run -d -p 127.0.0.1:8082:80 --name b nginx:1.27
# internal callers need neither -p: curl http://a from another container on the net
-p defaults to 0.0.0.0 — every interface
The single most common exposure mistake is a bare -p on a cloud host, which binds all interfaces and puts a “local” port on the public internet. Bind 127.0.0.1 for host-only access, put a firewall/security group in front of anything public, and remember that publishing is for host-reachability — inter-container traffic never needs it.