Skip to content

ss: socket statistics instead of deprecated netstat

When netstat hangs on a server with tens of thousands of connections, it’s time to switch to ss. Part of the iproute2 package, ss queries the kernel directly via netlink instead of parsing /proc/net/*. The result is instant output with minimal overhead.

Why switch from netstat

netstat from net-tools relies on a deprecated approach: it reads from /proc/net/tcp, /proc/net/unix and converts numeric IDs to symbolic names. On a server with active connections, this takes seconds and spikes CPU usage.

ss communicates with the kernel through a netlink socket. One call, structured data returned. For 10,000 connections, the difference is 0.02 seconds versus 3–5 seconds.

Note

netstat is officially marked as deprecated in most distributions. iproute2 is the current standard for network management in Linux.

Separate installation is rarely needed: ss ships with iproute2, which is present in every Linux by default.

Basic flags: the -tulnp equivalent

No need to relearn everything — flags are similar, just with more flexible ordering:

# Listening ports, show processes
ss -tlnp
FlagWhat it shows
-tTCP sockets
-uUDP sockets
-lListening sockets only
-nNumeric addresses and ports (no DNS)
-pProcess owner (PID, name)
-aAll sockets (not just listening)
-eExtended info (uid, inode)
-oTimer information

Flag order doesn’t matter — -tlnp and -ltnp are the same. -p needs root to show processes owned by other users.

Output differs structurally from netstat:

State      Recv-Q   Send-Q   Local Address:Port   Peer Address:Port   Process
LISTEN     0        128      0.0.0.0:22           0.0.0.0:*          users:(("sshd",pid=1234,fd=3))
LISTEN     0        511      127.0.0.1:6379       0.0.0.0:*          users:(("redis-server",pid=5678,fd=6))

Key columns:

ColumnMeaning
Recv-QBytes in receive buffer, not yet read by application
Send-QBytes in send buffer, not acknowledged by peer
Local Address:PortLocal end of the connection
Peer Address:PortRemote end
Tip

Non-zero Recv-Q or Send-Q on an established connection signals a problem. The application isn’t keeping up with reads, or the network is congested.

Full set of basic filters:

ss -t       # TCP only
ss -u       # UDP only
ss -w       # raw sockets
ss -x       # Unix sockets
ss -a       # all (listening and established)
ss -l       # listening only

Filtering by state and port

This is where ss beats netstat. Filters are native, not piped through grep:

# Established connections only
ss -t state established

# Active connections (not listening)
ss -t state connected

# TIME_WAIT — the classic use case
ss -t state time-wait

# Everything except listening
ss -t state connected -s

State combinations:

# ESTABLISHED only, with process info
ss -t state established -p

# SYN-SENT, SYN-RECV — handshake issues
ss -t state syn-sent

# FIN-WAIT-1, FIN-WAIT-2
ss -t state fin-wait1,fin-wait2

# CLOSE-WAIT — connection hanging, waiting to close
ss -t state close-wait

# Grouped states
ss -tan 'state established or state time-wait'

Port filter — one of the most common:

# Who is listening on 443
ss -tlnp 'sport = :443'

# Who is connected to 5432 (PostgreSQL)
ss -tp 'dport = :5432'

# All connections to any port 80 or 443
ss -t 'sport = :80 or dport = :80 or sport = :443 or dport = :443'
Warning

The sport and dport filters work with numeric values. For ranges, use >= and <=: 'dport >= 3000 and dport <= 4000'.

Address filter:

# Connections from a specific IP
ss -tp 'src 192.168.1.100'

# Outbound connections (not from local network)
ss -tp 'not src 192.168.0.0/16'

Extended output: -e, -i, -s

For queue diagnostics and statistics:

# Detailed information (extended)
ss -teln

# Adds:
# - uid (user)
# - inode
# - timers (for keepalive, TIME_WAIT)
# - timeout

Output with -e for an established connection:

ESTAB 0 0 10.0.0.5:22 10.0.0.100:52431 users:(("sshd",pid=1820,fd=3)) uid=1000 ino=35234 sk=0xffff88003a2c8000 <->

Interface information (-i):

ss -ti 'dst 10.0.0.1'
ESTAB 0 0 10.0.0.5:22 10.0.0.100:52431
         ts sack hbrs pmtu cwnd rtt rttvar unacked
         wscale:7,7 pmtu:1500 rcvmss:1448 advmss:1448 cwnd:10
         send 0.4Mbps rcv_space:43690

Key metrics:

MetricDescription
cwndCongestion window
rttRound-trip time
pmtuPath MTU
rcv_spaceReceive buffer size
sendCurrent send rate

State statistics (-s):

ss -s
Total: 124 (kernel 128)
TCP:   45 (estab 38, closed 2, orphaned 0, synrecv 0, timewait 2)

Transport Total     IP          IPv6
*         128       -           -
RAW       0         0           0
UDP       12        8           4
TCP       43        38          5
INET      55        46          9
FRAG      0         0           0
Note

The timewait 2 in ss -s output is a quick way to assess connection buildup. If the number grows each time you run it — something isn’t closing connections properly.

Common use cases

Which process is listening on a port and which interface:

ss -tlnp 'sport = :3306'
State   Recv-Q  Send-Q  Local Address:Port  Peer Address:Port  Process
LISTEN  0       128     0.0.0.0:3306        0.0.0.0:*         users:(("mysqld",pid=2345,fd=18))
LISTEN  0       128     127.0.0.1:3306      0.0.0.0:*         users:(("mysqld",pid=2345,fd=17))

Listening twice — once on all interfaces, once on localhost. If you need external only — check bind-address in the config.

How many sockets in TIME_WAIT — and who they belong to:

ss -s | grep timewait
# or
ss -ant | awk '/TIME-WAIT/ {count++} END {print count}'

# Top destinations leaking TIME-WAIT
ss -tan state time-wait | awk '{print $5}' | sort | uniq -c | sort -rn | head -20
Tip

High TIME_WAIT is usually normal. If it causes issues — on the client side use setsockopt with SO_LINGER, or SO_REUSEADDR on the server. Flag -ttu shows timers.

Whether a connection is stalled:

ss -ti 'dst 10.0.0.50'

Check rtt and unacked. If unacked grows but rtt stays the same — packets aren’t arriving but aren’t being lost either. Most likely the remote side stopped reading from the socket.

Find the process that opened a connection to a specific host:

ss -tp 'dst 192.168.1.50'
State   Recv-Q  Send-Q  Local Address:Port  Peer Address:Port  Process
ESTAB   0       0       10.0.0.5:45678      192.168.1.50:443   users:(("curl",pid=9876,fd=3))

Aggregated statistics by process:

ss -tnp | awk 'NR>1 {print $6}' | sort | uniq -c | sort -rn | head -10

Shows how many connections each process holds. Useful for finding processes opening too many sockets.

Quick reference for everyday tasks:

# netstat -tulnp  →  ss -tlnp
# netstat -ulnp   →  ss -ulnp

# What is listening
ss -tlnp

# Active connections
ss -tnp

# Connections to port
ss -t 'dport = :80'

# TIME_WAIT count
ss -s | grep timewait

# Process on port
ss -tlnp 'sport = :8080'

# Keep-alive / TIME_WAIT timers
ss -tno