When a service fails with “address already in use,” you need to know what is listening. When outbound traffic looks wrong, you need open connections. When packets go nowhere, you need the routing table and interface counters. That is what ss and netstat are for — they expose sockets, routes, and link stats from the kernel’s networking stack.

On modern Linux, prefer ss (from iproute2). It is faster and usually already installed. Treat netstat as the familiar alias map for older hosts and muscle memory — it often lives in a separate net-tools package and may be missing on minimal images. The sections below lead with ss, then show the matching netstat form when the same job matters.

Warm-up: list listening TCP sockets

Your playground is the live network stack on the machine in front of you. Start with listeners only — that answers “is my app bound?” without drowning you in every established client.

List TCP listening sockets, numeric addresses and ports (no DNS, no service-name lookups):

ss -ltn

The classic equivalent:

netstat -ltn

Typical ss output looks like this (addresses and ports will differ):

State   Recv-Q Send-Q Local Address:Port  Peer Address:Port
LISTEN  0      128        127.0.0.1:631        0.0.0.0:*
LISTEN  0      511          0.0.0.0:22         0.0.0.0:*
LISTEN  0      511             [::]:22            [::]:*

Read the columns with intent: State (LISTEN here), Local Address:Port (where the service binds), and Peer (wildcards for listeners). 0.0.0.0 or * means all IPv4 interfaces; 127.0.0.1 means localhost only.

Note: -l limits to listening sockets, -t to TCP, -n keeps everything numeric. That trio is the everyday “what ports are open?” starting point.

Show process owners

A bare listen list tells you the port. Adding process info tells you who owns it — the difference between “something is on 8080” and “old Java still holds 8080.”

Include process name and PID with -p:

ss -ltnp
netstat -ltnp

Sample shape (you often need sudo to see processes owned by other users):

State   Recv-Q Send-Q Local Address:Port  Peer Address:Port  Process
LISTEN  0      511          0.0.0.0:22         0.0.0.0:*      users:(("sshd",pid=1024,fd=3))
LISTEN  0      4096         0.0.0.0:8080       0.0.0.0:*      users:(("java",pid=4402,fd=51))

Note: When the failure is “address already in use,” run sudo ss -ltnp and find the port. For a deeper open-file view of the same conflict, see Linux lsof (lsof -nP -i :PORT). Prefer ss when you think in sockets and states; prefer lsof when you already think in file descriptors.

All TCP connections

Listeners are only half the story. Established sessions, half-closed sockets, and TIME-WAIT leftovers show who you are talking to and what is still winding down after a busy deploy.

List all TCP sockets (listening and connected), still numeric:

ss -tn
netstat -tn

You will see states such as ESTAB / ESTABLISHED, TIME-WAIT, and CLOSE-WAIT mixed with LISTEN. On a busy host, TIME-WAIT can dominate — that is usually normal TCP cleanup, not a mystery leak by itself.

Note: Start with -ltn when you only care about servers. Drop the -l when you are chasing a client connection, a stuck peer, or “why is this host talking to that IP?”

Filter by state and port

Once the table is large, filter before you scroll. ss understands socket states and simple address expressions; grep still works on both tools when you want a quick substring.

Show only established TCP connections:

ss -tn state established

Find whatever is listening on port 8080:

ss -ltn 'sport = :8080'

A portable grep form that also works with netstat:

ss -ltnp | grep ':8080'
netstat -ltnp | grep ':8080'

UDP has no LISTEN state the same way TCP does, but you still need “who bound this datagram port?” Use -u and keep -l / -n / -p as needed:

ss -lunp
netstat -lunp

Note: Quote ss filter expressions so the shell does not eat =. When filters feel fiddly under pressure, ss -ltnp | grep ':PORT' is a reliable fallback.

Summary counts

Sometimes you do not need every row — you need a pulse check: how many TCP sockets, how many in TIME-WAIT, how much UDP.

Print a short socket summary:

ss -s

Typical output includes totals by family and TCP state breakdown (exact labels vary by version):

Total: 312
TCP:   48 (estab 12, closed 0, orphaned 0, timewait 28)

Note: Reach for ss -s during incident triage before dumping thousands of lines. If TIME-WAIT or orphan counts look extreme relative to your service’s norms, then drill into ss -tn.

Routing table

Socket tools answer “what is connected?” Routing answers “where would this packet go next?” ss does not replace the routing table view — use ip route on modern systems, or netstat -rn on hosts where that is still the habit.

Show the route table with numeric destinations (no DNS):

ip route
netstat -rn

Sample ip route shape:

default via 192.168.1.1 dev eth0 proto dhcp metric 100
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.50

Note: Prefer ip route going forward. Keep netstat -rn in your head for older documentation and recovery environments that still ship net-tools.

Interface statistics

When connections look fine but traffic still fails, check the link counters: RX/TX errors, drops, and overruns often explain flaky NICs or saturated queues.

Classic netstat interface table:

netstat -i

The modern equivalent with richer counters:

ip -s link

Look for climbing errors, dropped, or overrun fields while you reproduce the fault. A quiet interface with rising drops is a stronger signal than another pass over the socket list.

Note: Use interface stats when the symptom is loss, resets, or “works on one NIC path only.” Use ss when the symptom is bind failures, unexpected peers, or wrong listen addresses.

Permissions and packages

Process columns (-p) need enough privilege to inspect sockets owned by other users. If Process is blank for system services, retry with sudo:

sudo ss -ltnp

ss ships with iproute2 (the same family as ip). netstat usually comes from net-tools. On a minimal container image, ss may exist while netstat does not:

command -v ss
command -v netstat

Note: Learn the ss flags as your default. Map them to netstat only when you land on a host that has net-tools and no muscle memory for iproute2 yet.

Quick reference card

Keep this nearby until the flags become muscle memory:

Goalss (prefer)netstat / ip
TCP listenersss -ltnnetstat -ltn
Listeners + processss -ltnpnetstat -ltnp
All TCP socketsss -tnnetstat -tn
Established onlyss -tn state establishednetstat -tn | grep ESTABLISHED
Port filterss -ltn 'sport = :8080'netstat -ltn | grep ':8080'
UDP listenersss -lunpnetstat -lunp
Summaryss -s
Routing tableip routenetstat -rn
Interface statsip -s linknetstat -i

Practice drills

Use your own machine (with sudo when process columns are empty) and try these without peeking. The point is to choose the view with intent, not to memorize every flag under pressure.

  1. List TCP listening sockets with numeric ports, including process names.
  2. Show only established TCP connections.
  3. Find whether anything is listening on UDP port 53 (or another DNS port you expect).
  4. Print the socket summary, then the default route.
  5. Display interface counters and note whether any RX/TX errors are non-zero.

When you are ready to compare, here are solid answers — not the only ones, but clear and portable:

sudo ss -ltnp
ss -tn state established
ss -lunp | grep ':53'
ss -s; ip route | grep default
ip -s link

If you can work through those five comfortably, you already cover most real ss / netstat work: listen checks, connection triage, UDP binds, routing peeks, and link-stat sanity. Default to ss, fall back to netstat on older hosts, and reach for ip when the question is routes or interface counters rather than sockets.