The netstat and ss commands are your primary tools for peering into the live network activity on a Linux system, but ss is the modern, faster successor to the older netstat.

Let’s see what’s happening right now. Imagine a web server running on your machine, listening for incoming HTTP requests on port 80.

# On the server machine, run this to see who's connected
ss -tulnp 'sport = :80'

Output might look like this:

Netid State      Recv-Q Send-Q Local Address:Port Peer Address:Port
tcp   LISTEN     0      128    0.0.0.0:80          0.0.0.0:*      users:(("nginx",pid=1234,fd=3))

This tells you nginx (process ID 1234) is listening on all network interfaces (0.0.0.0) on port 80. The Recv-Q and Send-Q show the number of bytes in the receive and send queues, respectively. LISTEN means it’s waiting for connections.

Now, let’s say a client connects.

# On a client machine, try to connect to the server
curl http://your_server_ip

On the server, if you run ss -tulnp 'sport = :80' again, you’ll see a new line for the established connection:

Netid State      Recv-Q Send-Q Local Address:Port Peer Address:Port
tcp   ESTAB      0      0      192.168.1.100:80    192.168.1.50:54321 users:(("nginx",pid=1234,fd=3))
tcp   LISTEN     0      128    0.0.0.0:80          0.0.0.0:*      users:(("nginx",pid=1234,fd=3))

The ESTAB state indicates an active, established connection. The Peer Address:Port shows the client’s IP address (192.168.1.50) and the ephemeral port it used (54321).

The core problem these tools solve is visibility: when things go wrong, you need to see what is connected to what, and why a connection might be failing or consuming resources. They bridge the gap between your application code and the network stack, showing you the actual state of sockets.

ss works by directly querying kernel data structures that track socket information. This is why it’s so much faster than netstat, which historically had to parse /proc filesystem entries. ss uses the netlink socket interface to get this information efficiently.

Here are the key flags you’ll use:

  • -t: Show TCP sockets.
  • -u: Show UDP sockets.
  • -l: Show listening sockets.
  • -n: Don’t resolve service names (show port numbers).
  • -p: Show the process using the socket.
  • -a: Show all sockets (listening and non-listening).
  • -s: Print summary statistics.

Combining them, ss -tulnp is a very common and powerful command: show TCP (t) and UDP (u) sockets that are listening (l), numerically (n), with the process information (p).

You can filter extensively. For example, to see all connections involving a specific IP address:

ss -tunap 'src 192.168.1.50 or dst 192.168.1.50'

Or connections to a specific remote port:

ss -tunap 'dport = :443'

The ss command also excels at showing socket statistics, which can be invaluable for diagnosing performance issues.

ss -s

Output might look like this:

Total: 338 (kernel 372)
TCP:   2 (estab 1, closed 0, orphaned 0, synrecv 0, timewait 0/0), ports 0
...

This summary shows the total number of sockets, broken down by type. The estab count under TCP is crucial for seeing active connections. If this number is unexpectedly high, it might indicate a runaway process or a denial-of-service attack.

When you’re troubleshooting a service that’s not responding, the first thing to check is whether it’s actually listening on the correct port and interface. For instance, if your application is supposed to be on 0.0.0.0:8080 but you only see 127.0.0.1:8080, it will only accept connections from the local machine.

# Check if a service is listening on the expected port
ss -tulnp | grep ':8080'

If the output is missing, the process might not have started correctly, or it might have failed to bind to the port. If it’s listening on the wrong IP, you’ll need to reconfigure the application to bind to 0.0.0.0 (all interfaces) or the specific external IP address.

The ss command can also reveal connections that are stuck in unusual states, like SYN-RECV or CLOSE-WAIT. A large number of CLOSE-WAIT connections, for instance, often indicates that an application is not properly closing its end of a connection after the peer has closed its end, leading to resource exhaustion.

# Find sockets stuck in CLOSE-WAIT
ss -t state close-wait

If you see many of these, you’d then use the -p flag to identify the culprit process and investigate its code for proper socket handling.

The kernel keeps track of socket send and receive buffers. You can see their sizes in the Recv-Q and Send-Q columns. If these queues are consistently full, it suggests a bottleneck: either the sending side is producing data faster than the receiving side can consume it, or the network link itself is saturated.

# See connections with a large send queue, indicating the peer isn't reading fast enough
ss -t '(ss.send_queue > 10240)'

This command, using an expression, filters for TCP sockets where the send queue is greater than 10240 bytes. A large Send-Q value, especially for an ESTAB connection, means your application is sending data faster than the client can receive it, or that the network path between them is congested.

A subtle but important detail is the difference between 0.0.0.0 and 127.0.0.1 for listening addresses. 0.0.0.0 means the socket will accept connections on all available network interfaces (e.g., eth0, wlan0, lo). 127.0.0.1 (localhost) means it will only accept connections originating from the local machine itself. If a service is bound to 127.0.0.1 but you’re trying to connect to it from another machine, it will fail.

The next logical step after understanding live connections is to investigate historical connection attempts or patterns, which often involves diving into firewall logs or packet capture tools like tcpdump.

Want structured learning?

Take the full Computer Networking course →