Quick Summary: The error
curl: (7) Failed to connect to localhost port <port>: Connection refusedindicates that your client machine attempted to establish a TCP handshake on a local port, but the operating system kernel immediately responded with a TCP reset (RST) packet. In local development and containerized environments, this most commonly occurs when an application inside Docker binds strictly to the container’s isolated loopback interface (127.0.0.1) instead of all network interfaces (0.0.0.0), when host port forwarding is omitted, or when the container crashed on startup.
Understanding the Root Causes
Unlike a network timeout (where packets vanish without a response), Connection refused is an active rejection: the host network stack confirms that no process is listening on the targeted IP and port. In modern Docker, microservice, and web workflows, the primary triggers include:
- Binding to 127.0.0.1 Inside Containers: Many development frameworks (FastAPI, Flask, Vite, Node.js, Rails) default to binding to
localhost(127.0.0.1). Inside a container, 127.0.0.1 is the container’s private namespace; Docker’s external port forwarder cannot relay traffic to it unless the app listens on0.0.0.0(INADDR_ANY). - Missing or Inverted Port Mapping: Running a container without the
-p <host_port>:<container_port>argument or inverting the parameters indocker-compose.yml. - Early Container Exit or Silent Crash: The service encountered a startup exception (e.g., missing database connection, syntax error) and terminated immediately, leaving the exposed port dead.
- Docker Desktop macOS / Windows Host Networking Quirk: Using
network_mode: "host"on macOS or Windows, which binds to the hidden lightweight Linux VM instead of the macOS/Windows host network.
Step 1: Quick Fix – Bind Container Services to 0.0.0.0
Verify that your backend framework or web server is explicitly configured to listen on all interfaces (0.0.0.0) rather than 127.0.0.1:
# Node.js / Express example:
# Change: app.listen(3000, '127.0.0.1') -> app.listen(3000, '0.0.0.0')
# Python / FastAPI (Uvicorn):
uvicorn main:app --host 0.0.0.0 --port 8000
# Python / Flask:
flask run --host=0.0.0.0 --port=5000
# Next.js / Vite:
npm run dev -- --host 0.0.0.0
Step 2: Verify and Correct Docker Port Publishing
Ensure that Docker’s port-mapping syntax properly connects the host’s port to the container’s internal listening port:
# Correct syntax: -p <HOST_PORT>:<CONTAINER_PORT>
docker run -d -p 8080:8000 --name my-app my-image:latest
# In docker-compose.yml:
services:
web:
image: my-image:latest
ports:
- "8080:8000" # Maps host localhost:8080 to container port 8000
# Check active port forwards across running containers:
docker port my-app
Step 3: Inspect Container Health and Listening Sockets
If the configuration looks correct but connections are still rejected, confirm that the container process is actively running and inspect its internal sockets:
# 1. Check if the container crashed or exited:
docker ps -a -f name=my-app
# 2. Inspect recent stdout and stderr logs for crash traces:
docker logs --tail 50 my-app
# 3. Enter the running container and check active listening ports:
docker exec -it my-app ss -tulpn
# Or using netstat:
# docker exec -it my-app netstat -tlpn
# Expected output inside container:
# LISTEN 0 511 0.0.0.0:8000 0.0.0.0:* (MUST be 0.0.0.0, NOT 127.0.0.1)
Verification & Testing Steps
Test the connection locally using verbose cURL flags to trace the TCP handshake and verify response headers:
# Execute verbose cURL request against the published host port
curl -Iv http://localhost:8080
# Expected output verifying successful TCP connection:
# * Trying 127.0.0.1:8080...
# * Connected to localhost (127.0.0.1) port 8080
# > GET / HTTP/1.1
# > Host: localhost:8080
# < HTTP/1.1 200 OK
# Test socket reachability using netcat:
nc -zv 127.0.0.1 8080
Troubleshooting Summary Comparison
| Observed Behavior | Root Cause | Corrective Action |
|---|---|---|
Connection refused on host; works inside container |
App bound to 127.0.0.1 inside container |
Configure server bind host to 0.0.0.0 |
docker port returns empty output |
Missing -p publishing flag |
Add -p <host_port>:<container_port> to docker run |
Immediate rejection; container status Exited (1) |
Application startup crash | Inspect docker logs <container> for exceptions |
network_mode: "host" fails on Mac/Windows |
Host networking isolated to Docker VM | Use standard port mapping (-p) instead of host mode |
Leave a Reply