Session 6 · Port mapping and a real web server#
The big idea#
A container has its own network, so a server listening on port 80 inside a container is invisible
from your computer unless you publish the port. -p HOST:CONTAINER connects a port on your machine to
a port in the container. Without it the service runs but nobody outside can reach it.
Everyday example: an office building with a reception desk. The meeting room (the container) is inside; visitors can’t walk straight in. Reception (the host) has a phone extension, 8088, that is forwarded to the meeting room’s line, 80. Dial the extension and you are connected. No forwarding, no calls.
nginx, reached from the host#
docker rm -f dm-web >/dev/null 2>&1
echo '--- run nginx and publish container port 80 as host port 8088'
docker run -d --name dm-web -p 8088:80 nginx:alpine >/dev/null
sleep 2
docker port dm-web
echo '--- request the page from the host'
curl -s -o /dev/null -w 'HTTP status: %{http_code}\n' http://localhost:8088/
curl -s http://localhost:8088/ | grep -o '<title>.*</title>'
echo '--- the access log shows that request'
docker logs dm-web 2>&1 | grep 'GET /' | sed 's/^\([0-9.]*\) .*\] "\(.*\)" \([0-9]*\) .*/client \1 "\2" status \3/'
echo '--- without -p nothing is published'
docker run -d --name dm-web2 nginx:alpine >/dev/null
sleep 1
[ -z "$(docker port dm-web2)" ] && echo 'dm-web2 publishes no ports, so the host cannot reach it'
docker rm -f dm-web dm-web2 >/dev/null
--- run nginx and publish container port 80 as host port 8088
80/tcp -> 0.0.0.0:8088
80/tcp -> [::]:8088
--- request the page from the host
HTTP status: 200
<title>Welcome to nginx!</title>
--- the access log shows that request
client 172.17.0.1 "GET / HTTP/1.1" status 200
client 172.17.0.1 "GET / HTTP/1.1" status 200
--- without -p nothing is published
dm-web2 publishes no ports, so the host cannot reach it
What the output tells you:
-p 8088:80:docker portconfirms80/tcp -> 0.0.0.0:8088(and the same on IPv6). Anything that reaches port 8088 on this machine goes to nginx’s port 80.curlgot HTTP 200 and the nginx welcome page title. The container is serving real HTTP.The access log recorded both requests. The client address
172.17.0.1is the host’s side of Docker’s private network.docker logsis how you debug a server.A container started without
-phas no published ports (docker portprints nothing), so the host cannot reach it. It can still talk to other containers on the same Docker network.
Choosing ports#
The left number must be free on the host. If 8088 is taken, Docker refuses to start.
Use
-p 127.0.0.1:8088:80to allow only your own machine, and not other computers on your network.-P(capital) publishes every port the image declares to random free host ports;docker port NAMEshows which.Two containers can both listen on 80 inside; they just need different host ports (8088, 8089).
Common mistakes#
Swapping the numbers. It is
HOST:CONTAINER.-p 80:8088would try to take host port 80.Starting a service that listens on
127.0.0.1inside the container. Publishing needs the app to listen on0.0.0.0, or the forwarded traffic never reaches it.Forgetting that
EXPOSEin a Dockerfile only documents a port. It does not publish it;-pdoes.Port already in use.
bind: address already in usemeans something on the host owns that port. Pick another.Opening a database port to the world with
-p 5432:5432. Bind to localhost or keep it private.
Hands-on exercises#
Exercise 1. Run nginx on host port 9000 and fetch the page with curl.
Solution
docker run -d --name web9000 -p 9000:80 nginx:alpine
sleep 2
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:9000/
docker rm -f web9000
It prints 200.
Exercise 2. Run two nginx containers at once, on host ports 9001 and 9002. How many processes listen on port 80 inside?
Solution
docker run -d --name web1 -p 9001:80 nginx:alpine
docker run -d --name web2 -p 9002:80 nginx:alpine
docker port web1; docker port web2
docker rm -f web1 web2
Each container has its own network, so each nginx listens on its own port 80. The host ports 9001 and 9002 are the only things that must differ.
Exercise 3. Make nginx reachable only from your own machine.
Solution
docker run -d --name local -p 127.0.0.1:9003:80 nginx:alpine
docker port local
docker rm -f local
docker port shows 80/tcp -> 127.0.0.1:9003, so only localhost can connect.
Practice#
Take the Session 6 quiz. The original Tomcat page is in the reference section.