When dealing with Docker port mapping issues like the ERR_CONNECTION_REFUSED error, we need to systematically verify several components of the networking stack. The problem described suggests the container is running (as evidenced by docker top
showing Node processes), but the port forwarding isn't working as expected.
First, let's check if the container is actually running and what ports it claims to expose:
$ docker ps
CONTAINER ID IMAGE COMMAND STATUS PORTS
7d984e974f6a ghost "docker-entrypoint.s..." Up 2 minutes 0.0.0.0:8080->2368/tcp
If the STATUS column doesn't show "Up", that's our first problem. If it does, note the PORTS mapping.
Docker provides detailed network inspection capabilities:
$ docker inspect test-ghost | grep -A 10 "NetworkSettings"
"NetworkSettings": {
"Ports": {
"2368/tcp": [
{
"HostIp": "0.0.0.0",
"HostPort": "8080"
}
]
}
}
Sometimes the application inside the container isn't actually listening on the expected port. Let's verify:
$ docker exec -it test-ghost bash
root@7d984e974f6a:/# netstat -tuln
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp6 0 0 :::2368 :::* LISTEN
This confirms whether the Ghost server is listening on port 2368 inside the container.
On the host machine, verify port 8080 is actually being used by Docker:
$ sudo ss -tulnp | grep 8080
tcp LISTEN 0 128 0.0.0.0:8080 0.0.0.0:* users:(("docker-proxy",pid=1234,fd=4))
If nothing appears, Docker isn't properly binding to the host port. Try stopping and restarting the container with explicit IP binding:
$ docker stop test-ghost
$ docker run --name test-ghost -p 127.0.0.1:8080:2368 -d ghost
Ghost has specific networking requirements. Check its configuration:
$ docker exec test-ghost cat /var/lib/ghost/config.production.json
{
"url": "http://localhost:2368",
"server": {
"host": "0.0.0.0",
"port": 2368
}
}
The critical setting is "host": "0.0.0.0"
- without this, Ghost won't accept external connections.
If basic checks don't reveal the issue, try these advanced techniques:
# Check Docker logs for errors
$ docker logs test-ghost
# Test raw network connectivity
$ curl -v http://localhost:8080/
# Check iptables rules (Linux hosts)
$ sudo iptables -t nat -L -n -v
# Try connecting from another container
$ docker run --rm --network host alpine nc -zv localhost 8080
Here are frequent causes of this issue and their fixes:
- Port Already in Use: Try mapping to a different host port (e.g.,
-p 8081:2368
) - Firewall Blocking: Even with host firewall off, corporate networks or VPNs might interfere
- IPv6 Issues: Some applications bind only to IPv4 or IPv6
- Application Startup Delays: Add health checks or wait longer before testing
When running a Ghost container with port mapping -p 8080:2368
, you're experiencing connection refusal despite the container appearing to run normally:
$ docker run --name test-ghost -p 8080:2368 -d ghost
$ curl http://localhost:8080
curl: (7) Failed to connect to localhost port 8080: Connection refused
Before deep troubleshooting, always check these fundamentals:
# Verify container status
$ docker ps -a
# Check exposed ports
$ docker inspect --format='{{.Config.ExposedPorts}}' test-ghost
# Test internal connectivity
$ docker exec -it test-ghost curl http://localhost:2368
1. Application Binding Issue
Ghost (Node.js) might not be binding to all interfaces:
# Check the application logs
$ docker logs test-ghost
# Typical solution - modify the startup command:
$ docker run --name test-ghost -e "NODE_ENV=development" -e "url=http://localhost:8080" -p 8080:2368 -d ghost
2. Port Mapping Verification
Confirm the mapping works both ways:
# Host perspective
$ netstat -tuln | grep 8080
# Container perspective
$ docker port test-ghost
$ docker exec test-ghost netstat -tuln
3. Firewall and Network Configuration
On Linux systems, check these potential blockers:
# Check Docker's network rules
$ sudo iptables -L -n
# Verify bridge network connectivity
$ docker network inspect bridge
When basic checks don't help:
# Run container interactively to debug
$ docker run -it --rm --entrypoint sh ghost
# Inside container:
$ ps aux
$ netstat -tuln
$ curl -v http://localhost:2368
Bypass potential localhost issues:
# Get container IP
$ docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{}' test-ghost
# Test directly (replace 172.17.0.2 with actual IP)
$ curl http://172.17.0.2:2368
The official Ghost image requires specific environment variables:
# Proper Ghost launch command
$ docker run -d \
--name test-ghost \
-e NODE_ENV=development \
-e url=http://localhost:8080 \
-p 8080:2368 \
ghost