Your application is failing. The logs say “connection refused” or “connection timed out”. Your first thought might be a bug in your code. It’s probably not. It’s probably the network.
When this happens people often reach for complex tools or start guessing. They restart services or reboot servers. This is a waste of time. There is a simple methodical way to find the root cause in minutes using a handful of commands that are already on every Linux server.
The first question is the most basic one. Can your server talk to the other server at all? Not your application. Just the machine itself. The tool for this is ping
.
ping
sends a tiny message called an ICMP packet to a target machine. If the target gets it it sends one back. It tells you two things. The target machine is on and there is a network path between your machine and it.
# Ping a server by its IP address
$ ping 192.168.1.10
# Or by its hostname
$ ping database.mydomain.com
If you get replies it’s working. The round trip time tells you the latency. If you get “Destination Host Unreachable” or 100% packet loss something is wrong at a very basic level. Either the server is off the network cable is unplugged or a core network device is misconfigured.
Sometimes ping
fails because a firewall is configured to block ICMP packets. So a failed ping doesn’t always mean the server is down. But a successful ping is a very good sign that basic connectivity exists.
Your application needs to connect to a specific service like a database or an API. That service runs on the target server and listens for connections on a specific port. If ping
works the next step is to see if your target service is actually running and listening for connections.
The modern tool for this is ss
. Some older systems use netstat
but ss
has replaced it. We want to see all listening TCP sockets.
# On the server running the service (e.g. the database server)
# -l shows listening sockets
# -t shows TCP sockets
# -n shows numeric ports instead of names (faster)
# -p shows the process using the socket
$ sudo ss -ltnp
You will get a list of all services listening for connections. You can filter this with grep
to find what you care about. For example if you’re looking for PostgreSQL which usually runs on port 5432.
$ sudo ss -ltnp | grep 5432
LISTEN 0 128 127.0.0.1:5432 0.0.0.0:* users:((“postgres”,pid=1234,fd=3))
This output is full of clues. The most important part is the local address 127.0.0.1:5432
. This tells you the PostgreSQL server is only listening for connections from the machine itself. It is not listening for external connections. This is a very common mistake. For a database to accept connections from your application server it needs to listen on 0.0.0.0:5432
or its specific public IP address.
The fact that a process is running does not mean it is accessible. You must always check what address and port it is bound to.
If ss
shows no process listening on the port you expect then your service is not running correctly. You need to check its logs to find out why.
So ping
works and ss
shows the service is listening on the correct IP address and port. The next question is whether a firewall is blocking the connection. This is the most common cause of “connection timed out” errors.
To test this you need a tool that tries to open a TCP connection. telnet
can do this but a better modern tool is nc
also known as netcat.
From your application server run nc
and tell it to check a specific server and port.
# -z tells nc to scan for listening daemons without sending data
# -v makes it verbose so it tells you what happened
$ nc -zv database.mydomain.com 5432
Connection to database.mydomain.com 5432 port [tcp/postgresql] succeeded!
This is the result you want. It means you can reach the port. Your application should be able to connect. If your app still fails the problem is likely higher up. Think authentication credentials or SSL certificates.
The more common result when you’re debugging is a timeout.
$ nc -zv database.mydomain.com 5432
nc: connect to database.mydomain.com port 5432 (tcp) failed: Connection timed out
A timeout almost always means a firewall is dropping your packets. The firewall could be on the database server itself like ufw
or iptables
. It could be on your application server. Or it could be a network security group in your cloud provider like AWS or GCP. You need to check each of these and add a rule to allow traffic on that port from your application server’s IP address.
Sometimes everything is configured correctly but you’re trying to connect to the wrong place. This happens when the hostname you are using resolves to the wrong IP address. This is a DNS problem.
The tool to debug DNS is dig
.
$ dig database.mydomain.com
Look at the ANSWER SECTION
. It will show you the IP address that the hostname resolves to.
;; ANSWER SECTION:
database.mydomain.com. 300 IN A 192.168.1.10
Make sure that 192.168.1.10
is the IP address you expect. If it’s wrong or if you get no answer section at all then you have a DNS configuration issue. Your server’s /etc/resolv.conf
might be pointing to the wrong DNS resolver or the DNS record itself might be incorrect.
Let’s put this together. Your new web app can’t connect to its database.
ping <database_ip>
. It works. The server is reachable.sudo ss -ltnp | grep 5432
. It shows postgres listening on 0.0.0.0:5432
. The service is listening correctly.nc -zv <database_ip> 5432
. It times out. This is the problem. A firewall is blocking the connection.sudo ufw status
. You see it is active and does not have a rule for port 5432.sudo ufw allow from <web_server_ip> to any port 5432
. The connection now works.This simple ordered process avoids guesswork. It isolates the problem layer by layer. Is the network there? Is the service listening? Is the port open? Is the name correct? Almost every connection problem can be solved by answering these four questions in order.
— Rishi Banerjee
September 2025