Port Mapping
A service can be running perfectly inside a container and still be completely unreachable from outside. That’s because a container’s ports are isolated from the host and external network by default. In this article, we’ll see how Docker’s port mapping connects the outside world to services running inside containers—and what happens behind the scenes when a request travels through that path.
What is Port Mapping?
Port mapping is the process of making a port inside a Docker container accessible through a port on the Docker host.
A container can run a service such as nginx and listen on port 80 inside the container. However, that does not automatically mean that other machines can connect to that service.
For example, if nginx is listening on:
Container
↓
TCP :80we can publish that port on the Docker host:
Docker Host :8080
↓
Container :80The Docker command for this is:
docker container run -d -p 8080:80 nginxHere:
8080:80
│ │
│ └── Container port
└─────── Host portThis means:
Connections to port
8080on the Docker host are forwarded to port80of the container.
Port mapping is also commonly called port publishing.
Why Port Mapping is Useful?
Containers are isolated from the host and from external networks by default. This is useful because applications inside containers don’t automatically expose their ports to the outside world.
Port mapping provides a controlled way to expose a containerized service.
Common use cases include:
- Exposing a web application running inside a container.
- Accessing an
nginxor Apache web server from another machine. - Exposing an API running inside a container.
- Connecting to a development database from the host.
- Running multiple containers that use the same internal port.
- Controlling which host ports are exposed externally.
For example, you can run two nginx containers that both listen on port 80 internally:
Host :8080 → Container A :80
Host :8081 → Container B :80The containers don’t need different internal ports because their network namespaces are separate.
How to Port Map Containers?
The basic syntax is:
docker container run -p <host-port>:<container-port> <image>For example:
docker container run -d -p 8080:80 nginxThis publishes port 80 inside the container on port 8080 of the Docker host.
You can verify the published port with:
docker container lsYou should see something similar to:
PORTS
0.0.0.0:8080->80/tcpYou can also inspect the container:
docker container inspect <container-id>Or extract the port mapping:
docker container port <container-id>You may get:
80/tcp -> 0.0.0.0:8080Binding to a Specific Host Address
By default:
-p 8080:80usually publishes the port on all host interfaces.
You can explicitly specify the host address:
docker container run -d -p 0.0.0.0:8080:80 nginxOr bind it only to localhost:
docker container run -d -p 127.0.0.1:8080:80 nginxThe second example means that the service can be accessed from the Docker host itself, but it isn’t directly published on the host’s external network interfaces.
Note
Publishing a port does not automatically make the service reachable from every network. Host firewalls, cloud security groups, routing, and other network policies can still block the connection.
What Happens in the Networking Stack?
When you run:
docker container run -d -p 8080:80 nginxDocker creates a network path between the host’s port 8080 and the container’s port 80.
A simplified view looks like this:
Other Machine
|
| TCP :8080
↓
Docker Host
|
| Port publishing / NAT
↓
Docker bridge network
|
↓
Container
|
| TCP :80
↓
nginxThe container normally has its own network namespace and receives its own IP address on the Docker bridge network.
For example:
Docker Host
192.168.1.10
Container
172.17.0.2nginx listens on:
172.17.0.2:80When Docker publishes:
192.168.1.10:8080 → 172.17.0.2:80a connection arriving at the host’s port 8080 is translated and forwarded toward the container.
On Linux, Docker commonly implements this using the host’s networking facilities, including network namespaces, virtual Ethernet interfaces, bridges, routing, and packet-filtering/NAT rules.
The exact implementation can vary depending on the Docker networking configuration, operating system, and Docker version, so it is better to think of port publishing as a logical host-port → container-port forwarding mechanism rather than relying on one specific kernel rule.
Port Mapping: Working Example with nginx
Let’s see this in action using an nginx container.
Step 1: Start nginx Without Port Mapping
Start an nginx container without publishing any ports:
docker container run -d --name nginx1 nginxCheck the running container:
docker container lsYou will notice that there is no published port in the PORTS column.
nginx is still listening on port 80 inside the container, but that port has not been published on the Docker host.
Step 2: Get the Container IP
We can inspect the container:
docker container inspect nginx1Or extract its IP address directly:
docker container inspect \
--format '{{.NetworkSettings.IPAddress}}' \
nginx1Suppose Docker gives us:
172.17.0.2We can verify that nginx is responding from the Docker host:
curl http://172.17.0.2You should receive the default nginx HTML response.
However, this does not mean that another machine on the network can access 172.17.0.2.
Step 3: Try From Another Machine
Suppose the Docker host has this IP:
192.168.1.10From another machine, you might try:
curl http://172.17.0.2This will normally fail.
Why?
Because 172.17.0.2 is the container’s address on Docker’s private bridge network. It is not the Docker host’s LAN address, and machines outside the Docker host generally don’t have a route to that private container network.
This is an important distinction:
Container IP
172.17.0.2
↑
Private Docker network
Docker Host IP
192.168.1.10
↑
Reachable from LANSimply knowing the container IP does not publish the service externally.
Step 4: Start nginx With Port Mapping
Let’s remove the first container:
docker container rm -f nginx1Now start another nginx container and publish port 80:
docker container run -d \
--name nginx2 \
-p 8080:80 \
nginxCheck the container:
docker container lsYou should see something similar to:
PORTS
0.0.0.0:8080->80/tcpThis tells us:
Docker Host :8080
↓
Container :80
↓
nginxStep 5: Inspect the Container
We can still retrieve the container IP:
docker container inspect \
--format '{{.NetworkSettings.IPAddress}}' \
nginx2Suppose it returns:
172.17.0.2Notice that the container IP is still a private Docker-network address.
The important difference is that we have now published the container’s port.
Step 6: Access From Another Machine
From another machine on the same network, use the Docker host’s IP and the published port:
curl http://192.168.1.10:8080The request travels approximately like this:
Other Machine
192.168.1.20
|
| HTTP :8080
↓
Docker Host
192.168.1.10:8080
|
| Port publishing
↓
Docker bridge
172.17.0.2:80
|
↓
nginxThis time, the request can reach the Docker host, where Docker forwards the traffic to the container.
Important
Do not use the container IP when testing access from another machine. Use the Docker host’s reachable IP address plus the published host port.
In this example:
❌ http://172.17.0.2:80
✅ http://192.168.1.10:8080If the second request still fails, check the Docker host’s firewall, cloud security-group rules, network routing, and whether nginx is actually running.
The Complete Picture
Without port mapping:
Other Machine
|
X
|
Docker Host
|
└── Docker Bridge → Container :80With port mapping:
Other Machine
|
| :8080
↓
Docker Host :8080
|
| Docker port publishing
↓
Container :80
|
↓
nginxThe key idea is simple:
A container port being open is not the same as a host port being published.
The container can have a service listening on port 80 while that service remains inaccessible from outside the Docker host. Port mapping creates the path that allows external clients to reach that service through a port on the host.