Diagnosing "Connection timed out"
What Does "Connection timed out" Mean?
Conclusion: You sent a connection request and nothing came back. The peer — or a device on the path — is silently discarding packets (DROP), or the peer is down. The kernel retries, then gives up. The defining trait is the multi-second to tens-of-seconds wait before it fails.
Connection timed out appears when you send a SYN to the destination, no SYN+ACK returns, and the kernel exhausts its retransmissions and gives up. At the system-call level, connect() returns ETIMEDOUT, which apps surface as "Connection timed out".
$ ssh user@192.0.2.10 ssh: connect to host 192.0.2.10 port 22: Connection timed out
$ curl http://192.0.2.10/ curl: (28) Failed to connect to 192.0.2.10 port 80 after 129000 ms: Connection timed out
What sets it apart from refused (rejected instantly) and No route to host (returned unreachable instantly) is that it fails after a long silence. That silence is the signature of "someone is dropping packets without a word" — almost always a firewall DROP or a missing peer.
Prerequisites
- OS: Ubuntu / a typical Linux (uses
ss/traceroute/tcpdump) - Target: a host you want to reach by IP and port
- Some checks need
sudo(tcpdump/nft, etc.)
How Is It Different from refused and No route to host?
Conclusion: Refused means "arrived but the port rejected it (instant RST)", No route to host means "there is no path (instant ICMP)", and timed out means "no reply at all (long wait)". Same failed connection — the time before a reply tells you which layer is at fault.
Connection errors fall into three families by how the reply comes back. Settle which one you have first.
| Error | What comes back | Layer of the cause |
|---|---|---|
| Connection refused | TCP RST (instant) | Service / port (L4, app) |
| No route to host | ICMP unreachable (instant) | Route / ARP / REJECT (L2–L3) |
| Connection timed out | No response (long wait) | DROP / silent path (L3–L4) |
$ time curl -sS http://192.0.2.10/
curl: (28) Failed to connect to 192.0.2.10 port 80 after 129000 ms: Connection timed out real 2m9.012s
If it takes several seconds or more to fail, you have the timed-out family: someone is silently discarding packets. If it failed instantly, go elsewhere. For a stopped service or closed port see Diagnosing Connection refused; for no path at all see Diagnosing No route to host. This article covers Connection timed out.
Refused is "the host is alive but the port turned me away"; timed out is "nobody answers at all". The former gives a clear reply (RST); the latter is silence. Silence is the hallmark of DROP — a firewall discarding with -j DROP, or a peer that is not there.
Why Does "Connection timed out" Happen?
Conclusion: The cause reduces to four things — ① a firewall DROP (most common), ② an unopened cloud security group, ③ a dead or non-listening peer, or ④ a blackhole on the path. Work from the layer nearest your host outward.
The paths that produce ETIMEDOUT, organized by cause:
| Cause | What is happening | Where to start |
|---|---|---|
| Firewall DROP | The server / a device on the path silently drops SYN | nft list ruleset |
| Unopened security group | A cloud SG / NACL does not allow the port | Cloud console |
| Dead / non-listening peer | The server is down / the service is not started | ss -ltn on server |
| Path blackhole | Router misconfig / a VPN or routing hole eats packets | traceroute / mtr |
Triage from the layer nearest your host outward. First confirm it really is timed out by the wait, then see how far on the path packets reach, and finally inspect the server side and its firewall.
DROP and REJECT are opposites. DROP discards the packet silently, so the client sees no reply and times out. REJECT returns an ICMP or RST, so the client sees No route to host or refused. In short: "a long silence = DROP", "an instant error = REJECT" — the symptom reveals how the firewall is discarding.
What Do You Check First? (Measure the Wait)
Conclusion: Cap the wait with
curl --connect-timeoutornc -wand test the connect alone. If it waits out the full timeout before failing, no-response (timed out) is confirmed. There is no need to sit through the long default.
First set a short, explicit timeout and test only the connect phase. Waiting out the default (about 2 minutes for curl) is a waste.
# Cap the connect establishment at 5 seconds $ curl -sS --connect-timeout 5 http://192.0.2.10/
curl: (28) Failed to connect to 192.0.2.10 port 80 after 5001 ms: Connection timed out
nc (netcat) gives an even more direct reachability check.
$ nc -vz -w3 192.0.2.10 80
nc: connect to 192.0.2.10 port 80 (tcp) failed: Connection timed out
If it fails only after the full wait (3 seconds above), the peer is returning nothing to your SYN. If it says refused instantly, the port is closed but the host is responding — that is not timed out.
--connect-timeout caps establishing the connection, while -m (--max-time) caps the whole transfer. For triage you want the connect phase only, so use --connect-timeout. The nc -w timeout likewise bounds the connect wait.
Where on the Path Does It Stop? (traceroute / mtr)
Conclusion: Use
traceroute/mtrto see how many hops your packets reach toward the destination. The point past which everything turns to* * *is the boundary where packets vanish (are DROPped).
Visualize where on the path packets disappear.
$ traceroute -n 192.0.2.10
1 10.0.0.1 0.4 ms 0.3 ms 0.3 ms 2 100.64.0.1 1.2 ms 1.1 ms 1.0 ms 3 * * * 4 * * *
If hops up to 2 answer and 3 onward are all * * *, packets are being silently discarded at that boundary. Beyond it is a blackhole, and a firewall or a routing hole is the suspect.
But traceroute uses ICMP/UDP, so in an environment where TCP passes but only ICMP is blocked, it can wrongly look like "it stops on the path". To probe with the port you actually want, use TCP mode.
# Trace with TCP SYN to destination port 80 $ sudo traceroute -T -p 80 -n 192.0.2.10
For a continuous view, mtr is handy.
$ mtr -n -T -P 80 192.0.2.10
If only the final hop is * * * while everything before it answers, the peer's firewall may drop ICMP yet pass TCP. Do not conclude DROP from "traceroute stops" alone — always confirm with the protocol and port you want to connect on (-T -p).
How Do You Confirm SYN Goes Unanswered? (tcpdump)
Conclusion: Watching real packets with
tcpdumpshows directly that SYN is sent but no SYN+ACK returns. "Sent but unanswered" on the client and "SYN never arrived" on the server narrow down where the DROP sits.
When you want proof, look at the packets directly. Capture on the client while you attempt the connection.
# Run in another terminal, then attempt the connection $ sudo tcpdump -ni any host 192.0.2.10 and port 80
10:00:00.100 IP 10.0.0.42.51000 > 192.0.2.10.80: Flags [S], seq 1, ... 10:00:01.100 IP 10.0.0.42.51000 > 192.0.2.10.80: Flags [S], seq 1, ... 10:00:03.100 IP 10.0.0.42.51000 > 192.0.2.10.80: Flags [S], seq 1, ...
Only Flags [S] (SYN) is retransmitted at growing intervals (1s, 2s, 4s…), and no Flags [S.] (SYN+ACK) returns. That is timed out itself: the packet does not reach the peer, or it arrives and is ignored.
If you can log in to the server, capturing on the server too, at the same time, makes it decisive.
# Run on the server $ sudo tcpdump -ni any tcp port 80
- SYN does not arrive on the server → DROP on the path (a router / cloud SG)
- SYN arrives but is not answered → the server's local firewall DROP, or no listener
Seeing the SYN retransmit intervals (the roughly 1, 2, 4, 8 second exponential backoff) is itself hard proof of "no response". The retry count is set by net.ipv4.tcp_syn_retries (default 6), and it governs the client's total wait.
How Do You Pin Down a Firewall DROP?
Conclusion: If it times out yet packets reach the peer, a
-j DROPrule is the prime suspect. On the server, checknft list ruleset/iptables -Lto see whether the port is allowed. In the cloud, the security group in front of the OS firewall is the cause far more often.
Inspect the filtering rules on the server.
# Inspect nftables rules $ sudo nft list ruleset | grep -i -E 'drop|policy' # On systems using iptables $ sudo iptables -L -n -v --line-numbers
Chain INPUT (policy DROP) pkts bytes target prot ... destination ... ... ACCEPT tcp ... tcp dpt:22
With policy DROP and only port 22 allowed, a SYN to port 80 is silently discarded and the client times out. If you use ufw, check what is allowed.
$ sudo ufw status verbose
For recovering when ufw locks out even SSH, see ufw SSH Troubleshooting.
In the cloud (AWS / GCP / Azure, etc.), the security group or network ACL in front of the OS firewall is by far the most frequent culprit. If nft list ruleset is empty yet it still times out, first check in the console that the cloud SG allows the port (and the source IP range). A security group defaults to "deny all inbound" (effectively DROP).
Is the Server Even Listening?
Conclusion: Even with the firewall open, nothing connects if the service is not running. On the server, check
ss -ltnto see whether the port is LISTENing and whether it is bound only to127.0.0.1.
If you can log in to the server, look directly at whether the port is listening.
$ ss -ltn
State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128 127.0.0.1:80 0.0.0.0:* LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
If port 80 is listening on 127.0.0.1:80, it accepts local connections only and nothing arrives from outside (opening the firewall will not help). Fix the service's bind setting to listen on 0.0.0.0:80 (all interfaces) or the relevant IP.
If the port is not in the list at all, the service is not running. Check its state.
$ systemctl status nginx
A 127.0.0.1 bind should normally produce an instant refused, not a timeout — but if a firewall in front of it DROPs, the symptom is masked as timed out. When "I opened the firewall but it still won't connect", always suspect the bind address.
Checklist When It Still Fails
Conclusion: Timed out means "no response". Working from your host outward — wait time → path → SYN reply → firewall → listener — collapses the cause into one of DROP, a security group, a dead service, or a path blackhole.
- [ ] Did it take several seconds or more to fail (an instant error is refused / No route to host)?
- [ ] Did you cap the connect phase with
curl --connect-timeout/nc -w? - [ ] Did you trace with
traceroute -T -p <port>on the port you actually want? - [ ] Did you find which hop turns to
* * *(the DROP boundary)? - [ ] Did
tcpdumpshow SYN retransmitting with no SYN+ACK? - [ ] Does SYN arrive on the server too (no = path / SG, yes = local FW / no listener)?
- [ ] Is there a
DROPfor the port innft list ruleset/iptables -L? - [ ] Does the cloud security group / NACL allow the port and source?
- [ ] Is the port LISTENing on
0.0.0.0(publicly) inss -ltn?