Docker Error Guide: 'Connection refused' — Container Can't Reach the Host
Fix a Docker container that can't reach host or peer services: use host.docker.internal with host-gateway, pick bridge vs host networking, publish ports, and open the firewall.
- #docker
- #troubleshooting
- #errors
- #networking
Fixing errors like this? Get 500 free DevOps AI prompts
500 copy-paste AI prompts for the stack you actually run — one PDF, free.
Overview
A container cannot reach a service running on the Docker host, or on another container. Application code that connects to localhost or 127.0.0.1 typically fails like this:
curl: (7) Failed to connect to localhost port 5432 after 0 ms: Connection refused
The trap is that inside a container localhost means the container itself, not the host. So a process trying to reach a database on the host, or a peer container by IP, hits nothing and gets Connection refused (or No route to host / DNS Name does not resolve).
Symptoms
Connection refusedwhen a container connects tolocalhost:<port>for a service that runs on the host.- One container can’t resolve another by name (
could not translate host name "db" to address). - The service is published but still unreachable from outside — or reachable from the host but not the container.
pingto another container’s IP times out, or the host firewall silently drops the packets.
Common Root Causes
localhostconfusion — the app targets127.0.0.1, which is the container’s own loopback, not the host or a peer.- Missing
host.docker.internalmapping — on Linux this name isn’t defined unless you add--add-host host.docker.internal:host-gateway. - Wrong network mode — containers on the default
bridgenetwork can’t resolve each other by name; only user-defined networks provide DNS. - Publish vs expose —
EXPOSE/--exposeonly documents a port; you need-p/--publishto actually map it to the host. - Host firewall / iptables — the host’s firewall blocks the
docker0bridge or the published port. - Service binds to
127.0.0.1on the host — a host service listening only on loopback is unreachable from containers even viahost-gateway.
Diagnostic Workflow
Confirm which network the container is on and its IP:
docker inspect --format '{{json .NetworkSettings.Networks}}' web-1 | jq
docker network ls
docker network inspect bridge | jq '.[0].Containers'
Test reachability from inside the container — this is the key step:
docker exec web-1 sh -c 'nc -zv host.docker.internal 5432'
docker exec web-1 sh -c 'curl -fsS http://api-1:8080/healthz'
docker exec web-1 sh -c 'getent hosts api-1 || nslookup api-1'
To reach a service on the host, add the gateway mapping (Linux):
docker run --add-host host.docker.internal:host-gateway myapp:1.4.2
docker exec web-1 sh -c 'nc -zv host.docker.internal 5432'
For container-to-container name resolution, use a user-defined network instead of default bridge:
docker network create appnet
docker network connect appnet web-1
docker network connect appnet api-1
docker exec web-1 getent hosts api-1
Verify the port is actually published and the host is listening:
docker port web-1
sudo ss -tlnp | grep 5432 # is the host service on 0.0.0.0 or 127.0.0.1?
Check the host firewall if traffic still drops:
sudo iptables -L DOCKER-USER -n -v
Example Root Cause Analysis
An app in web-1 connects to postgres://localhost:5432 and logs Connection refused. Postgres runs on the host, not in a container.
From inside the container, docker exec web-1 nc -zv host.docker.internal 5432 fails with host.docker.internal: Name does not resolve — the container was started without the gateway mapping on Linux. Restarting with --add-host host.docker.internal:host-gateway and pointing the app at host.docker.internal:5432 gets past DNS, but nc now reports Connection refused.
sudo ss -tlnp | grep 5432 on the host shows Postgres bound to 127.0.0.1:5432 only. Because it listens on host loopback, the host-gateway route can’t reach it. Binding Postgres to 0.0.0.0:5432 (or the docker bridge IP) and allowing the subnet in pg_hba.conf — plus a DOCKER-USER iptables allow rule — makes nc -zv host.docker.internal 5432 succeed. Root cause: a real DNS gap and a host service on loopback, both required for the fix.
Prevention Best Practices
- Put related containers on a user-defined network and address them by service name, never by
localhostor hard-coded IP. - To reach the host from a container, use
host.docker.internaland always pass--add-host host.docker.internal:host-gatewayon Linux. - Ensure host services you need from containers bind to
0.0.0.0(or the bridge IP), not127.0.0.1. - Remember
EXPOSEdocuments;-p host:containerpublishes. Publish only what you truly need externally. - Use
--network hostonly when you deliberately want the container to share the host’s stack — it removes network isolation. - Add explicit
DOCKER-USERiptables rules rather than relying on Docker’s defaults on locked-down hosts.
Quick Command Reference
# Networks and where the container sits
docker network ls
docker network inspect appnet | jq '.[0].Containers'
# Reach the host from inside a container (Linux)
docker run --add-host host.docker.internal:host-gateway myapp:1.4.2
docker exec web-1 nc -zv host.docker.internal 5432
# Container-to-container by name
docker network create appnet
docker network connect appnet web-1
docker exec web-1 getent hosts api-1
# Published ports and host listeners
docker port web-1
sudo ss -tlnp | grep 5432
# Firewall
sudo iptables -L DOCKER-USER -n -v
Conclusion
Container connectivity failures almost always come down to a naming or reachability mismatch: localhost inside a container is not the host, default bridge gives no name resolution, and a service bound to 127.0.0.1 is invisible across the bridge. Prove it from inside the container with docker exec … nc/curl, add host.docker.internal:host-gateway for host access, put peers on a user-defined network, and confirm the port is published and the firewall allows it. More networking recipes live in the Docker guides.
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.