"Connection timed out" vs "Connection refused": what each one tells you, and how to fix it
Timed out means a firewall dropped the packets; refused means nothing listens there. Tell them apart with nc and curl, then fix ufw, firewalld or the bind address.
by OhSudo ··
Not sure this is your cause?Find it step by step: Website down →Find it step by step: Can't connect over SSH →
You can’t reach a service on another machine, and the error is one of these:
curl: (28) Failed to connect to app01 port 8080 after 135168 ms: Couldn't connect to serverssh: connect to host app01 port 22: Connection timed outcurl: (7) Failed to connect to app01 port 8080 after 2 ms: Couldn't connect to servernc: connect to app01 port 8080 (tcp) failed: Connection refusedThey look alike, but they point to opposite ends of the problem. Refused comes back in milliseconds: your packet reached the machine, and the machine answered “nothing listens on this port” (a TCP reset). Timed out takes seconds or minutes: nothing answered at all — something on the way dropped the packet, usually a firewall. A third answer, No route to host, often means a firewall that rejects the packet out loud.
| You see | What happened | Look at |
|---|---|---|
Connection refused |
The machine answered: nothing listens on that address and port | The service: is it running, on which port, on which address? |
Connection timed out |
No answer: the packets were dropped | Firewalls: on the server (ufw, firewalld, iptables), in the cloud (security groups), on the network |
No route to host |
A firewall rejected it (or the host is down on the local network) | firewalld on RHEL/Rocky, REJECT rules |
- Clientconnects to app01:8080arrives
- app01nothing listens: answers with a reset(breaks here)
- Clientconnects to app01:8080sent
- A firewalldrops the packet(breaks here)never arrives(fails)
- app01never sees it
TL;DR
nc -zv -w 5 app01 8080 # timed out / refused / succeeded, in 5 seconds at mostnc -zv -w 5 app01 22 # is the machine itself reachable?sudo ss -tlnp | grep 8080 # on the server: listening? on 127.0.0.1 or 0.0.0.0?sudo ufw status # Ubuntusudo firewall-cmd --list-all # RHEL / Rocky / AlmaLinux1. Test from the client, with a short timeout
Without a limit, a timed-out connection hangs for about two minutes (the kernel retries the first packet several times). Give your test tools a few seconds instead:
Then test a port you know works — SSH is usually open:
If port 22 answers and 8080 times out, the machine is up and the network is fine: something drops traffic to that port only. That’s a firewall rule, almost always.
2. Check the service on the server
Log in to the server and look at what really listens:
LISTEN 0 4096 127.0.0.1:8080 0.0.0.0:*Two things to read here. The service runs, and it answers locally — so the app itself is fine. And it listens on 127.0.0.1: only programs on the same machine can reach it. Keep that in mind; it will come back in section 4. (Use sudo ss -tlnp to also see which process holds the port.)
3. Timed out: the firewall drops it
Ubuntu / Debian — ufw. With ufw active, everything that isn’t allowed is dropped: the client waits and times out.
Status: active
To Action From-- ------ ----OpenSSH ALLOW AnywhereOpenSSH (v6) ALLOW Anywhere (v6)Better still, allow only who needs it: sudo ufw allow from 10.0.0.0/24 to any port 8080 proto tcp.
RHEL / Rocky / AlmaLinux — firewalld. The default public zone rejects what isn’t allowed, so the client gets No route to host instead of a timeout:
ssh app01sudo firewall-cmd --list-allsudo firewall-cmd --permanent --add-port=8080/tcpsudo firewall-cmd --reload--permanent alone changes the saved configuration but not the running one, and a rule added without --permanent works only until the next reload or reboot. Add it with --permanent, then --reload — as above.
Note
In the cloud there’s a second firewall. AWS security groups, Azure network security groups and GCP firewall rules sit in front of the machine and drop traffic silently too. If the server’s own firewall allows the port and you still time out, allow the port there — and only from the addresses that need it.
Is the port one that should be reachable from outside at all? Databases, Redis or the Docker API should not — the Port Lookup tells you, port by port.
4. Refused: nothing listens on that address
After opening the firewall, try again from the client:
curl: (7) Failed to connect to app01 port 8080 after 0 ms: Couldn't connect to serverProgress: the packets now reach the machine, and the machine answers — “refused”. Remember section 2: the app listens on 127.0.0.1, so connections that arrive on the network interface find nothing there. Make it listen on all addresses (0.0.0.0), or on the server’s private address. Where that’s set depends on the app: a --bind/--host option, listen in nginx, listen_addresses in PostgreSQL, bind in Redis. Here it’s a config file:
ssh app01 cat /etc/myapp/config.envssh app01sudo sed -i "s/^BIND_ADDRESS=.*/BIND_ADDRESS=0.0.0.0/" /etc/myapp/config.envsudo systemctl restart myapp && ss -tln | grep 8080exitcurl -m 5 http://app01:8080/healthss now shows 0.0.0.0:8080, and the request from the laptop gets its answer.
Other reasons for “refused”: the service isn’t running at all (systemctl status, see Job for … failed), it listens on a different port than you think, or you’re connecting to the wrong machine (an old DNS record, a typo in the IP). The same applies to databases: see MySQL, SQL Server and MongoDB.
5. Inside containers
The same rules apply one level down. A container that publishes -p 127.0.0.1:8080:80 is reachable only from the host itself; an app inside a container that listens on 127.0.0.1 is unreachable even through a published port — it must listen on 0.0.0.0 in the container. And from inside a container, localhost is the container, not the host: see connecting from a container to localhost.
Shell, we have a problem!
Similar, but different: Address already in use is the server side — your service can’t start because another one holds the port: see who is using port 80. No route to host is usually a firewall that rejects: see that article. And Could not resolve host means the name never became an IP address: a DNS problem, see Temporary failure in name resolution.
$ ./practice.sh
Practice this in the terminal
Open the Terminal Playground with this article’s scenario loaded. It is safe and runs in your browser.
Found this useful? Share it.
Found a mistake? Let us know at contact@ohsudo.com.