Cannot Connect to Server Linux: Step-by-Step Troubleshooting
Troubleshoot 'cannot connect to server' on Linux — diagnose connection refused, timeout, and network unreachable errors with a systematic step-by-step approach.
Connection refused. Connection timed out. Network unreachable. Each error means something different and points to a different fix.
TL;DR: Read the Error First
| Error | Meaning | Start here |
|---|---|---|
Connection refused |
Server up, nothing listening on that port | Check if service is running |
Connection timed out |
Packets dropped (firewall, routing, server down) | Check firewall and routing |
Network unreachable |
No route to destination | Check routing table |
Name or service not known |
DNS resolution failed | Check DNS |
No route to host |
ARP failed or host down | Check if host is up |
Step 1: Confirm the Error Type
# Test connection
curl -v http://10.0.1.50:8080/health
nc -zv 10.0.1.50 8080
telnet 10.0.1.50 8080
The error message is your first diagnostic. Don't skip this — "connection refused" and "connection timed out" have completely different causes.
Step 2: Is the Host Reachable at All?
ping -c 3 10.0.1.50
# If ping fails:
# - host is down
# - ICMP is blocked by firewall
# - wrong IP address
# Check routing
ip route get 10.0.1.50
# Shows which interface and gateway will be used
traceroute 10.0.1.50
# Shows where packets stop
Step 3: Fix "Connection Refused"
Connection refused means the host is reachable but nothing is listening on that port.
# On the SERVER: is the service running?
systemctl status myapp
ss -tlnp | grep :8080
# Not running? Start it
systemctl start myapp
# Running but on wrong interface?
ss -tlnp | grep :8080
# 127.0.0.1:8080 — only accessible locally!
# Fix: change bind address in app config to 0.0.0.0
Bound to localhost is the most common cause. The service is running but only accessible from the server itself.
Step 4: Fix "Connection Timed Out"
Timeout = packets are being dropped. Usually a firewall.
# On the SERVER: check firewall
# Ubuntu (ufw)
ufw status
ufw allow 8080/tcp
# RHEL (firewalld)
firewall-cmd --list-all
firewall-cmd --permanent --add-port=8080/tcp
firewall-cmd --reload
# Raw iptables
iptables -L INPUT -n | grep 8080
iptables -I INPUT -p tcp --dport 8080 -j ACCEPT
# Is the server's firewall blocking?
# Test from the server itself:
curl localhost:8080 # works = firewall is blocking external
curl 10.0.1.50:8080 # still works = cloud security group issue
Cloud servers (AWS/GCP/Azure/Vercel) also have security groups / VPC firewall rules outside the OS. Check those in the cloud console.
Step 5: Fix DNS Issues
# Test DNS resolution
nslookup api.example.com
dig api.example.com
# If DNS fails but IP works:
curl http://api.example.com/health # fails
curl http://1.2.3.4/health # works
# Check DNS config
cat /etc/resolv.conf
# Flush DNS cache
systemd-resolve --flush-caches
# or
service nscd restart
Step 6: Check from Both Sides
If you have access to the destination server:
# ON THE DESTINATION SERVER:
# 1. Is the service listening?
ss -tlnp | grep :8080
# 2. Is the firewall blocking?
iptables -L INPUT -n -v | grep :8080
# 3. Are connections arriving?
tcpdump -i any port 8080 -n
# (run this, then trigger the connection from the client)
# If no packets show: network/routing issue
# If packets show but no response: app issue
tcpdump is definitive — if packets arrive at the server, the network is fine. If they don't, the problem is between the client and server.
Real Examples
App deployed, clients get "connection refused"
# Server side
ss -tlnp | grep :3000
# LISTEN 0 128 127.0.0.1:3000 ...
# ^^^^ bound to localhost only
# Fix: in app config (Node.js example)
# app.listen(3000, '0.0.0.0') ← was: app.listen(3000)
Works from server, times out from client
# From server: works
curl localhost:8080
# From client: times out
# → Firewall blocking external access
# On server (Ubuntu)
ufw allow from 10.0.0.0/8 to any port 8080
Intermittent timeouts under load
# Check connection states
ss -s
# TIME-WAIT: 14000 climbing → port exhaustion
# Check backlog
ss -tlnp | grep :8080
# Recv-Q: 128 → accept backlog full, new connections dropped
# Fix: increase backlog and reuse ports
echo "net.ipv4.tcp_tw_reuse = 1" >> /etc/sysctl.conf
echo "net.core.somaxconn = 65535" >> /etc/sysctl.conf
sysctl -p
Common Mistakes
Testing from the server itself and assuming external works the same. curl localhost:8080 succeeds but external connections get refused — the service is bound to 127.0.0.1.
Fixing the OS firewall but forgetting cloud security groups. AWS security groups, GCP firewall rules, and Azure NSGs are separate from iptables/firewalld.
Not checking which interface the service is bound to. 0.0.0.0 = all interfaces. 127.0.0.1 = localhost only. 10.0.1.50 = specific interface only.
Conclusion
Read the error message carefully — it tells you where to start. Connection refused = service not running or wrong bind address. Timed out = firewall or routing. Use tcpdump when you need ground truth about whether packets are actually arriving.
Related: Check Open Ports in Linux: ss vs netstat — confirm what's actually listening. strace, lsof, and ss: The Trio That Solves Every Mystery — socket-level debugging.