Docker Port Exposed But Not Accessible From Host
A port that shows up in docker ps but doesn't respond when you try to connect from the host almost always means the application inside the container is bound to the wrong network interface, or the port mapping itself was specified incorrectly — not that Docker's networking is broken.
The Problem
The container appears to be running with the expected port mapping:
docker ps
CONTAINER ID PORTS
a1b2c3d4e5f6 0.0.0.0:3000->3000/tcp
But trying to connect from the host fails:
curl http://localhost:3000
curl: (7) Failed to connect to localhost port 3000: Connection refused
Why It Happens
Docker's port mapping (the -p flag or ports: in Compose) forwards traffic to the container's network interface, but this only works if the application inside is actually listening on an interface that traffic can reach. Common causes:
- The application inside the container binds specifically to
127.0.0.1(localhost) rather than0.0.0.0—127.0.0.1inside a container refers only to the container's own internal loopback, which Docker's port forwarding can't reach from outside, even with a correct port mapping in place - The port mapping direction was specified backwards, or the container's internal port doesn't actually match what's in the mapping (a common typo:
-p 3000:8080when the app inside actually listens on 3000, not 8080) - The application crashed shortly after starting, so nothing is actually listening on the port anymore, even though the container itself still shows as running
- A host-level firewall is blocking the mapped port, independent of anything Docker itself is doing correctly
The Fix
First, confirm the application inside the container is actually listening on 0.0.0.0, not 127.0.0.1. Check directly from inside the container:
docker exec -it your-container netstat -tlnp
If the output shows something like 127.0.0.1:3000 rather than 0.0.0.0:3000, that's the root cause — the fix is in your application's own configuration, telling it to bind to all interfaces rather than just loopback:
# Example: Node.js Express app
app.listen(3000, '0.0.0.0'); // not app.listen(3000) which often defaults to localhost only
Different frameworks and languages have their own specific way of setting this — the key requirement is the same everywhere: the process must bind to 0.0.0.0 (or an equivalent "all interfaces" setting) for Docker's port forwarding to have anything reachable to forward traffic to.
Confirm the port mapping itself is correct, matching container-internal-port to host-port in the right order:
docker run -p 3000:3000 your-image
# format is: -p HOST_PORT:CONTAINER_PORT
Confirm the application is actually still running and hasn't crashed silently after container startup, by checking logs directly:
docker logs your-container
A container can remain in "running" status even if the main process inside crashed and something else (a shell, a supervisor process) keeps it technically alive — checking logs reveals whether the actual application is genuinely still up and listening.
Rule out a host-level firewall blocking the port, particularly relevant on cloud VM instances with their own separate security group or firewall configuration layered on top of the OS firewall:
sudo ufw status
sudo iptables -L -n
Still Not Working?
If binding, mapping, and firewall all check out but the connection still fails, test connectivity directly from inside the container first, bypassing the host-level port mapping entirely, to isolate whether the application itself is the problem or the mapping layer:
docker exec -it your-container curl http://localhost:3000
If this succeeds from inside the container but the same request fails from the host, the issue is specifically in the port mapping or host-level network path, not the application itself — pointing you back toward re-checking the exact -p flag syntax and any intermediate firewall rules, rather than further investigating the application's own configuration.
It's also worth checking whether another process on the host is already bound to the same host-side port, silently preventing Docker's own port forwarding from working as expected. Confirm nothing else on the host is already listening on the port you're trying to map to:
sudo lsof -i :3000
If this shows a non-Docker process already bound to that port, Docker's port mapping can conflict with it in ways that produce inconsistent or confusing connection behavior, rather than a clear, immediate error at container startup.
Finally, if you're testing from a different machine on the same network rather than the host itself, confirm the host's own network interface is actually reachable and that you're not being blocked by a router-level or cloud provider's separate network security configuration, which exists independently of both Docker's port mapping and the host's own local firewall rules covered earlier.