When a website does not load or a service cannot reach its database, guessing wastes time. A short, ordered set of checks tells you whether the problem is on the server itself, in DNS, somewhere on the path, or in the service and its firewall. In this tutorial you will work through that order on Ubuntu 24.04 with ip, ping, dig, traceroute, mtr, ss, netstat and nc, and learn how to read what each command prints.

Prerequisites

To follow this guide you need:

  • A server running Ubuntu 24.04 LTS, such as a CubePath VPS. The commands also work on Debian 12; package names for Rocky Linux 9 are noted where they differ.
  • A non-root user with sudo privileges.
  • Console access from your provider's panel in case the network problem also cuts your SSH session.

The examples use example.com as a remote host, 203.0.113.10 as a remote IP address and eth0 as the network interface. Replace them with your own values.

The troubleshooting order

Work from the inside out and stop at the first check that fails:

CheckQuestion it answersTool
1. Local configurationDoes the server have an address, a route and DNS servers?ip, resolvectl
2. ReachabilityDo packets get to the destination and back?ping
3. Name resolutionDoes the name resolve to the right address?dig
4. PathWhere along the way are packets lost or delayed?traceroute, mtr
5. SocketsIs the service listening, and what connections exist?ss, netstat
6. PortCan a TCP connection to the service port be opened?nc

Step 1 - Installing the tools

Ubuntu 24.04 ships ping and ss, but not all the others. Install them in one go:

sudo apt update
sudo apt install iputils-ping traceroute mtr-tiny bind9-dnsutils net-tools netcat-openbsd

net-tools provides the legacy netstat, bind9-dnsutils provides dig and mtr-tiny is mtr without the graphical interface. On Rocky Linux 9 the equivalent is sudo dnf install iputils traceroute mtr bind-utils net-tools nmap-ncat.

Step 2 - Checking the local network configuration

Most "the network is down" problems on a single server are a missing address, a wrong default route or broken DNS settings. Start with the interfaces:

ip -br address
lo               UNKNOWN        127.0.0.1/8 ::1/128
eth0             UP             198.51.100.20/24 2001:db8:10::20/64 fe80::be24:11ff:fe4a:10c2/64

The interface must be UP and have the address you expect. Next, the routing table:

ip route
default via 198.51.100.1 dev eth0 proto static
198.51.100.0/24 dev eth0 proto kernel scope link src 198.51.100.20

Without a default via line the server can only reach its own subnet. To see which route and source address the kernel would use for a given destination, ask it directly:

ip route get 203.0.113.10

Finally, check which DNS servers systemd-resolved is using:

resolvectl status

Look for a DNS Servers: line under the global section or the interface. If it is empty, name resolution fails even when the network is fine.

Step 3 - Testing reachability with ping

ping sends ICMP echo requests and measures how long replies take. Always limit the count with -c so the command ends on its own:

ping -c 4 203.0.113.10
PING 203.0.113.10 (203.0.113.10) 56(84) bytes of data.
64 bytes from 203.0.113.10: icmp_seq=1 ttl=57 time=12.4 ms
64 bytes from 203.0.113.10: icmp_seq=2 ttl=57 time=12.1 ms
64 bytes from 203.0.113.10: icmp_seq=3 ttl=57 time=12.3 ms
64 bytes from 203.0.113.10: icmp_seq=4 ttl=57 time=12.2 ms

--- 203.0.113.10 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 12.1/12.2/12.4/0.1 ms

The summary line matters most. Packet loss above 0% on a wired link, or a large mdev (jitter), points to congestion or a faulty link. Ping the default gateway first (the address from ip route), then an external IP, then a name: the first step that fails tells you how far the problem reaches.

Useful variations:

ping -c 4 -6 example.com
ping -c 4 -M do -s 1472 203.0.113.10

The first forces IPv6. The second sends 1,500-byte packets with the "don't fragment" bit set (1,472 bytes of data plus 28 bytes of headers). If it fails with message too long while smaller packets work, there is an MTU problem on the path, a common cause of connections that open but hang when transferring data.

Step 4 - Checking name resolution with dig

If pinging an IP address works but pinging a name fails, the problem is DNS. Query the name with dig:

dig example.com A +short
93.184.215.14

Compare the answer from your configured resolver with a public one to find out whether the local resolver is the problem:

dig @1.1.1.1 example.com A +short

To see which nameservers are authoritative and follow the whole delegation from the root, use +trace:

dig example.com +trace

In the full (non +short) output, check the status: field in the header: NOERROR means the name exists, NXDOMAIN means it does not, and SERVFAIL usually means a broken or unreachable authoritative server or a DNSSEC failure.

Step 5 - Finding where packets are lost with traceroute and mtr

traceroute lists every router (hop) between you and the destination by sending probes with increasing TTL values:

traceroute -n example.com
traceroute to example.com (93.184.215.14), 30 hops max, 60 byte packets
 1  198.51.100.1  0.412 ms  0.388 ms  0.371 ms
 2  192.0.2.33  1.102 ms  1.095 ms  1.201 ms
 3  * * *
 4  203.0.113.77  11.884 ms  11.902 ms  11.870 ms
 5  93.184.215.14  12.231 ms  12.198 ms  12.240 ms

-n skips reverse DNS lookups, which makes the output faster and easier to read. A hop with * * * followed by hops that answer is normal: that router simply does not reply to probes. Only stars that continue until the end of the trace indicate a real block or loss.

By default traceroute uses UDP probes, which firewalls often drop. When the trace stops early, probe the actual service port with TCP instead (this requires root):

sudo traceroute -n -T -p 443 example.com

A single trace is a snapshot. mtr combines ping and traceroute and sends many probes to every hop, which is what you want to prove intermittent loss or to send evidence to a provider:

mtr -rwn -c 100 example.com
HOST: web01                     Loss%   Snt   Last   Avg  Best  Wrst StDev
  1.|-- 198.51.100.1             0.0%   100    0.4   0.4   0.3   0.9   0.1
  2.|-- 192.0.2.33               0.0%   100    1.1   1.2   1.0   3.4   0.3
  3.|-- ???                     100.0   100    0.0   0.0   0.0   0.0   0.0
  4.|-- 203.0.113.77             0.0%   100   11.9  12.0  11.8  14.1   0.3
  5.|-- 93.184.215.14            0.0%   100   12.2  12.3  12.1  13.0   0.2

-r prints a report, -w uses wide host names, -n disables DNS and -c 100 sends 100 probes per hop. Read loss from the end: if the final hop shows 0% loss, loss on an intermediate hop is only that router rate-limiting its replies. Loss that starts at one hop and continues on every hop after it is real loss at that point of the path.

Step 6 - Inspecting sockets with ss

Once the path is fine, look at the service itself. ss is the modern replacement for netstat and reads socket information straight from the kernel, so it is fast even on busy servers.

List every listening TCP and UDP socket with the owning process:

sudo ss -tulnp
Netid State  Recv-Q Send-Q Local Address:Port  Peer Address:Port Process
udp   UNCONN 0      0         127.0.0.54:53         0.0.0.0:*     users:(("systemd-resolve",pid=612,fd=16))
tcp   LISTEN 0      4096         0.0.0.0:22         0.0.0.0:*     users:(("sshd",pid=880,fd=3))
tcp   LISTEN 0      511          0.0.0.0:80         0.0.0.0:*     users:(("nginx",pid=1204,fd=6))
tcp   LISTEN 0      151        127.0.0.1:3306       0.0.0.0:*     users:(("mysqld",pid=1033,fd=21))

The options are -t TCP, -u UDP, -l listening only, -n numeric ports and -p process (needs sudo to see other users' processes). Pay attention to the local address: 127.0.0.1:3306 accepts connections only from the server itself, while 0.0.0.0 or * accepts them on every interface. A service bound to loopback is a very common reason for "connection refused" from outside.

Filter by port to check one service:

sudo ss -tlnp 'sport = :443'

List established connections, optionally filtered by remote address:

ss -tn state established
ss -tn dst 203.0.113.10

Count TCP connections per state to spot anomalies:

ss -tan | awk 'NR > 1 {print $1}' | sort | uniq -c | sort -rn
    412 ESTAB
    128 TIME-WAIT
     12 LISTEN
      3 SYN-RECV

Thousands of TIME-WAIT sockets are normal on a busy web server. A growing number of SYN-RECV sockets suggests a SYN flood or clients that cannot complete the handshake, and many CLOSE-WAIT sockets point to an application that does not close its connections.

For a quick overall summary, use ss -s.

netstat equivalents

netstat still works after installing net-tools, and you will find it in older documentation. The common commands map directly:

Tasknetstatss / ip
Listening ports with processsudo netstat -tulnpsudo ss -tulnp
All TCP connectionsnetstat -tanss -tan
Summary statisticsnetstat -sss -s (and nstat)
Routing tablenetstat -rnip route
Interface countersnetstat -iip -s link

Prefer ss and ip on new systems: net-tools is no longer developed and is not installed by default.

Step 7 - Testing a TCP port with nc

ping only proves ICMP works. To know whether a client can actually open a connection to a service, test the port with nc from the client side:

nc -zv -w 3 203.0.113.10 443
Connection to 203.0.113.10 443 port [tcp/https] succeeded!

-z only checks the connection without sending data, -v prints the result and -w 3 gives up after three seconds. The failure message tells you where to look:

  • Connection refused: the packet reached the host but nothing listens on that port, or the service is bound to another address. Check with ss -tlnp on the server.
  • timed out: packets are being dropped, usually by a firewall on the server (sudo ufw status verbose), a network firewall or a security group in front of it.

For an HTTP service, curl goes one layer further and shows DNS, connection and TLS details:

curl -sv -o /dev/null https://example.com

Troubleshooting

  • ping: socket: Operation not permitted in a container: the container lacks the NET_RAW capability. Run the check from the host or use nc instead.
  • traceroute shows only stars from the first hop: outbound UDP is filtered. Retry with sudo traceroute -T -p 443 or use mtr --tcp -P 443.
  • ss -p shows no process names: you ran it without sudo, so only your own processes are visible.
  • Name resolution works with dig @1.1.1.1 but not without it: the local stub resolver or upstream servers are wrong. Check resolvectl status and the netplan configuration in /etc/netplan/.

Conclusion

You now have a repeatable order for network problems: confirm the local configuration with ip, test reachability with ping, verify DNS with dig, locate loss with traceroute and mtr, and check the service with ss and nc. As next steps, learn to capture packets with tcpdump when these tools are not enough, review your firewall rules with UFW or nftables, and save an mtr report of your normal paths so you have a baseline to compare against during the next incident.