Topic 4.2
Publishing Ports
In one line
EXPOSE documents a port; -p actually makes it reachable from outside the container. Getting the host:container order backwards is the single most common Docker networking mistake.
Think of it like this
A hotel room's phone extension (the container's internal port) versus the hotel's public phone number with an extension code (the published host port). Dialing the hotel's public number and entering the extension routes your external call to that specific internal room — that mapping (public number → internal extension) is exactly what -p sets up.
Key ideas
- 01
The syntax
-p HOST_PORT:CONTAINER_PORTis easy to get backwards.-p 8080:80means 'requests to MY machine's port 8080 get forwarded to port 80 INSIDE the container' — if your app inside the container actually listens on port 3000, this mapping is simply wrong and nothing will work, no matter how correct the HOST side looks. - 02
You can publish to a DIFFERENT host port than the container uses internally — e.g.
-p 9090:8080lets you reach a container's internal port 8080 via your host's port 9090, useful when you're running multiple containers that all internally use the same conventional port (like several web apps all using 8080 internally) but need distinct host-facing ports. - 03
-p 8080:8080(without specifying a host IP) publishes on ALL of your host's network interfaces — meaning it's reachable not just fromlocalhostbut potentially from your local network too.-p 127.0.0.1:8080:8080restricts it to localhost only — a meaningful security distinction on a shared or exposed development machine. - 04
docker run -P(capital P, no port numbers) automatically publishes ALL ports the image's Dockerfile declared with EXPOSE, each to a RANDOM available host port — convenient for quick testing, but you'll needdocker port <container>afterward to find out which random host ports actually got assigned. - 05
A container's internal port and its published host port are COMPLETELY INDEPENDENT numbers with no required relationship — the only thing that matters is that your app INSIDE the container is actually listening on whatever port you specify as the CONTAINER_PORT half of the mapping.
In your stack
- →
A Spring Boot app defaults to listening on port 8080 internally (configurable via
server.port). A very common real command:docker run -p 80:8080 myapp:v1— this lets users reach the app via the standard web port 80 on your host, while the app itself continues running on its normal, unmodified port 8080 inside the container.
Code & diagrams
The exact host:container order, illustrated with concrete examples.
# Standard case: host port matches container port
docker run -d -p 8080:8080 myapp:v1
# Publish to a DIFFERENT host port (e.g. running two instances of the same image)
docker run -d --name app1 -p 9001:8080 myapp:v1
docker run -d --name app2 -p 9002:8080 myapp:v1
# Both containers internally use 8080; externally reachable at 9001 and 9002
# Restrict to localhost only — not reachable from your local network
docker run -d -p 127.0.0.1:8080:8080 myapp:v1
# Publish EVERY exposed port to random host ports, then look up what was assigned
docker run -d -P myapp:v1
docker port <container-name-or-id>
# 8080/tcp -> 0.0.0.0:49153
# COMMON MISTAKE: backwards order — app listens on 8080, but this maps host 80 to CONTAINER port 80
docker run -d -p 80:80 myapp:v1 # WRONG if the app inside actually listens on 8080!Explain it without notes
You run docker run -p 3000:8080 myapp and your Spring Boot app (default port 8080) is unreachable at localhost:3000. Given the app's actual configured port, what's most likely wrong?
Why does -p 127.0.0.1:8080:8080 behave differently from plain -p 8080:8080 on a machine connected to a shared network?
Practice
Run the same image twice with two different host ports (like the app1/app2 example) and confirm both are independently reachable.
Deliberately reverse a port mapping you know is correct (swap host and container port) and observe the exact failure — get comfortable with what 'wrong direction' looks like.
Trade-offs
- ↔
Binding port publishes to
0.0.0.0(all interfaces) is the default and is fine for most local development, but on a machine you don't fully trust the network of, explicitly binding to127.0.0.1costs one extra bit of typing for a real security improvement.
Done when you can
I can write a
-pflag correctly without having to guess which side is host and which is container.I know the difference in reachability between binding to 0.0.0.0 versus 127.0.0.1.
When a port mapping 'doesn't work', I check the container's actual listening port before suspecting Docker.