Skip to content

netstat

A server is supposed to be listening on port 8080, and a deploy just finished — is it actually up, and is anything else already sitting on that port? netstat was, for decades, the default answer to exactly that kind of question: what's listening, what's connected to what, and which process owns it.

What netstat actually shows

netstat reports active network connections and listening sockets, pulling its information from the same kernel data structures the network stack itself uses. The combination most people reach for is -tulpn:

sudo netstat -tulpn
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      612/sshd
tcp        0      0 127.0.0.1:5432          0.0.0.0:*               LISTEN      1140/postgres
tcp6       0      0 :::8080                 :::*                    LISTEN      2087/node
udp        0      0 0.0.0.0:68              0.0.0.0:*                           734/dhclient
udp        0      0 127.0.0.53:53           0.0.0.0:*                           951/systemd-resolve

Each flag narrows what's shown: -t restricts to TCP, -u adds UDP, -l limits TCP output to listening sockets (without it, established connections show too), -p shows the owning process ID and name, and -n prints numeric addresses and ports instead of resolving them to names — worth using by default, since name resolution on every row can make the command noticeably slower and pull in DNS as a dependency for a command that's often run precisely because DNS is suspect.

Recv-Q and Send-Q are queue depths in bytes: data received but not yet read by the application, and data queued to send but not yet acknowledged by the peer, respectively. Both sitting near zero on a healthy connection is normal; Recv-Q climbing and staying high is a sign the application isn't reading its socket fast enough, and Send-Q climbing is a sign the peer isn't acknowledging — either a slow or unresponsive receiver, or a network problem between the two.

Note that sudo is required here specifically because of -p — without root, netstat can still show which ports are listening, but the process name and PID column is blank for sockets owned by other users, since revealing that mapping is itself sensitive information about what another user is running.

netstat is legacy — ss is the replacement

netstat's own man page says this plainly, in its NOTES section: it calls itself "mostly obsolete" and names direct replacements — ss for netstat itself, ip route for netstat -r, and ip -s link for netstat -i. netstat comes from the net-tools package, largely unmaintained since the mid-2000s; ss comes from iproute2, the actively maintained package behind the modern ip command, and reads socket information more efficiently — netstat on a host with tens of thousands of open connections can be noticeably slower than ss doing the equivalent query, because of how each tool walks the kernel's socket tables.

The same query with ss, which gets a full article of its own next:

sudo ss -tulpn
Netid  State    Recv-Q  Send-Q   Local Address:Port    Peer Address:Port  Process
udp    UNCONN   0       0        0.0.0.0:68             0.0.0.0:*          users:(("dhclient",pid=734,fd=6))
udp    UNCONN   0       0        127.0.0.53:53           0.0.0.0:*          users:(("systemd-resolve",pid=951,fd=13))
tcp    LISTEN   0       128      0.0.0.0:22              0.0.0.0:*          users:(("sshd",pid=612,fd=3))
tcp    LISTEN   0       244      127.0.0.1:5432          0.0.0.0:*          users:(("postgres",pid=1140,fd=5))
tcp    LISTEN   0       511      [::]:8080               [::]:*             users:(("node",pid=2087,fd=21))

The columns carry the same meaning as netstat's — Recv-Q/Send-Q are the same queue depths, Process is the same PID/name mapping -p provides — but ss groups UDP and TCP sockets together rather than under separate headings, and the Send-Q column for a LISTEN row means something slightly different: it's the configured backlog (the maximum number of pending, not-yet-accept()-ed connections the kernel will queue), not a per-connection send buffer.

Practical scenario: a service that won't start

A deploy script reports success, but the new service on port 8080 isn't answering. sudo ss -tulpn | grep 8080 comes back empty — nothing is listening there at all, ruling out a firewall or routing problem before either is even considered, since the socket was never opened in the first place. Checking the service's own logs shows why:

sudo journalctl -u myapp -n 5
Sep 03 10:12:04 web01 myapp[2087]: Error: listen EADDRINUSE: address already in use :::8080

Something else already had port 8080 bound when the new process tried to start. sudo ss -tulpn | grep :8080 against the previous deploy's leftover process — a crashed instance that never released its socket — would have shown exactly that conflict directly, which is the reason to check what's listening before assuming a fresh deploy failed for some more complicated reason.

Common mistakes

  • Running netstat or ss without sudo and concluding no process owns a listening port. Without root, the process column is often just blank for other users' sockets — a blank Process field isn't the same as "nothing is listening."
  • Confusing Send-Q on a LISTEN row with a per-connection send buffer. On a listening socket, it's the accept backlog size — an entirely different number from Send-Q on an established connection.

Practice exercises

  1. Run sudo ss -tulpn on a machine you control and identify which listening sockets are bound to 0.0.0.0 (all interfaces) versus 127.0.0.1 (loopback only) — the same binding distinction this course has already flagged as a common source of "it works locally but not from another machine" reports.
  2. Two services both try to bind port 9000; the second one fails to start. Using ss, write the single command that would show you which process already holds that port.
  3. Explain, in your own words, why ss is generally faster than netstat on a host with a very large number of open connections.

Knowing what's listening on a machine you can already reach is one problem. A completely different one is figuring out why you can't reach a machine at all — which router along the way is the one dropping traffic. That's next.

Sources