16.2.2.1 Runtime Port Missing
A focused guide to Runtime Port Missing, connecting core concepts with practical Docker and container operations.
Runtime port missing describes a container that is running, with an application that appears to have started successfully, but that cannot actually be reached on the expected port, either from the host or from other containers, a problem that is almost always traceable to a mismatch between what the application is actually listening on, what the container exposes, and what the host has actually published.
Confirming the application is listening at all
Before investigating Docker's own port configuration, confirming the application process itself is genuinely listening on a port inside the container rules out the simplest possible explanation, that the application never started a listener successfully despite otherwise appearing to run:
docker exec my-api netstat -tlnp
tcp 0.0.0.0:3000 LISTEN 1/node
If no listener appears at all, the problem is inside the application itself, not in anything Docker-related, and the investigation should shift toward application logs and startup behavior rather than port configuration.
Listening on localhost instead of all interfaces
A very common cause of an apparently missing port is an application configured to listen only on 127.0.0.1 (localhost) rather than 0.0.0.0 (all interfaces), which means it is only reachable from within its own network namespace, not from outside the container even when Docker's own port publishing is configured correctly:
app.listen(3000, '127.0.0.1'); // only reachable from inside the container itself
app.listen(3000, '0.0.0.0'); // reachable from outside the container as well
tcp 127.0.0.1:3000 LISTEN
A netstat or ss output showing the listener bound specifically to 127.0.0.1 rather than 0.0.0.0 is the direct, conclusive evidence of this specific misconfiguration; changing the application's own listen address resolves it directly.
EXPOSE versus actual port publishing
The EXPOSE instruction in a Dockerfile is purely documentation, declaring which ports the image's author intends the container to listen on; it does not actually publish anything or make a port reachable from outside the container on its own:
EXPOSE 3000
docker run my-api
Running a container from this image without an explicit -p flag results in a container with no port actually published to the host, regardless of what EXPOSE declares; the port must be explicitly published at run time:
docker run -p 3000:3000 my-api
Port mapping direction confusion
The -p flag's argument order, host_port:container_port, is a frequent source of confusion, and reversing it accidentally publishes the wrong port or attempts to publish a port that does not match what the application is actually listening on inside the container:
docker run -p 8080:3000 my-api
This makes the application, listening on port 3000 inside the container, reachable on port 8080 on the host; attempting to reach port 3000 directly on the host would fail, since nothing was published there, only port 8080 was mapped through to the container's internal port 3000.
docker ps
PORTS
0.0.0.0:8080->3000/tcp
Checking the actual port mapping reported by docker ps directly confirms which host port maps to which container port, removing any ambiguity about which port should actually be used to reach the service from outside.
Port already in use on the host
A container that fails to start with its expected port published, or that starts but the port mapping silently does not work as expected, can be caused by the desired host port already being occupied by another process or container:
docker run -p 3000:3000 my-api
Error: bind: address already in use
lsof -i :3000
docker ps --filter "publish=3000"
Identifying whatever else is currently using the desired host port, whether another container or an entirely unrelated host process, and either stopping it or choosing a different host port for the new mapping resolves this directly.
Container-to-container reachability without host publishing
For two containers needing to reach each other, host port publishing is not actually necessary or relevant at all; containers on the same Docker network can reach each other directly by container or service name on whatever port the target container's application is listening on internally, regardless of whether that port has been published to the host:
docker exec my-api curl http://my-db:5432
A misunderstanding here, assuming a port needs to be published to the host for one container to reach another, leads to unnecessarily exposing internal services to the host or external network when only internal, container-to-container reachability was actually needed.
Firewall and security group interference
For a port that is correctly published according to Docker's own configuration but still unreachable from outside the host entirely, a host-level firewall or, in a cloud environment, a security group or network ACL, may be blocking the traffic before it even reaches Docker's own port mapping:
iptables -L DOCKER -n
curl http://localhost:3000
curl http://<host-external-ip>:3000
A successful connection from localhost on the host itself, combined with a failed connection from an external address, points specifically toward a firewall or network-level restriction between the external client and the host, rather than anything wrong with Docker's own port configuration.
Common mistakes
- Assuming
EXPOSEin a Dockerfile actually publishes a port, when it is purely documentation with no effect on actual port accessibility without an explicit-pflag at run time. - Configuring the application to listen only on
127.0.0.1instead of0.0.0.0, making it unreachable from outside the container regardless of Docker's own port publishing configuration. - Confusing the host and container port positions in the
-pflag's argument order, publishing the wrong port or mapping to a port the application is not actually listening on. - Not checking for a host port conflict with another process or container before assuming Docker's own port mapping mechanism is malfunctioning.
- Unnecessarily publishing a port to the host when only container-to-container reachability on the same Docker network was actually needed.
Runtime port missing problems are resolved by checking, in order, whether the application is actually listening, on which address, whether the port was actually published correctly and in the right direction, and whether anything at the host or network level, a conflicting process or a firewall rule, is blocking access despite Docker's own configuration being entirely correct.