Published port refuses connections: the app binds 127.0.0.1, not 0.0.0.0
Medium

Problem

You deployed the web service and published it with -p 8080:8000. The container is Up, docker ps shows the mapping 0.0.0.0:8080->8000/tcp, and the app logs say it's listening — but every request from the host is refused:

$ curl -sS http://localhost:8080/
curl: (56) Recv failure: Connection reset by peer

The publish mapping looks right. Find out why the port is unreachable and state the fix.

Initial setup

  • webmyapp:2.3 (gunicorn), Up 2 hours, 0.0.0.0:8080->8000/tcp.
  • The host cannot reach http://localhost:8080.

Example interaction

$ docker ps
# web is Up, PORTS: 0.0.0.0:8080->8000/tcp   (mapping present)

$ docker port web
8000/tcp -> 0.0.0.0:8080                      (forward configured)

$ docker exec web ss -tlnp
State  Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
LISTEN 0      2048       127.0.0.1:8000      0.0.0.0:*    users:(("gunicorn",pid=1,fd=5))

The listener is on 127.0.0.1, not 0.0.0.0.

Acceptance

You've solved it when:

  • You've shown the app binds the container's loopback (127.0.0.1:8000)
rather than all interfaces (0.0.0.0:8000) — via `docker exec web ss -tlnp (or netstat -tlnp`). A published port forwards to the container's eth0, so a loopback-only listener can never be reached from the host. NOT misdiagnosed as a wrong -p mapping, a host firewall, or a Docker networking bug (the mapping is correct — docker port web confirms it).
  • You've stated the fix: rebind the app to 0.0.0.0 (e.g. gunicorn
--bind 0.0.0.0:8000, Flask --host=0.0.0.0) and recreate the container.

Constraints

  • Tools: docker CLI only.
  • docker restart web does NOT help — the app still binds 127.0.0.1 on
restart. The bind address is the app's config, not Docker's.
  • The -p 8080:8000 mapping is correct; don't chase the publish flag or a
host firewall. The problem is INSIDE the container.

Follow-up

  1. Why does a published port reach 0.0.0.0:8000 but not 127.0.0.1:8000
inside the container's network namespace?
  1. Why is 127.0.0.1 a common framework default (gunicorn, Flask dev
server, Rails/Puma), and why is it a safe default OUTSIDE a container?
  1. How would a healthcheck that curls 127.0.0.1:8000 from inside the
container pass while the host still gets connection-refused?
Live session
Code
SavedNo commands yet