cheat sheet
ps, ss & netstat
Inspect running processes (ps), list network connections and listening ports (ss / netstat). Covers output formats, filtering, process trees, and socket state analysis.
ps, ss & netstat — Processes & Sockets
What it is
ps, ss, and netstat are standard Linux system inspection utilities for monitoring running processes and network connections. ps (process status) is POSIX-standard and available on every Unix system, shipped on Linux via the procps-ng package (current release 4.0.6, January 2026) alongside pgrep, pkill, top, and vmstat. ss (socket statistics) is the modern Linux replacement for netstat, querying the kernel directly over a netlink socket — significantly faster than walking /proc/net/*. netstat was deprecated back in 2001 and has been removed from the default install on most current distributions; reach for ps to see what processes are running and their resource usage, ss to list open ports and active network connections, and netstat only on older systems or on macOS/BSD where it is still standard.
ps — Process Status
Syntax styles
ps accepts both BSD (ps aux) and POSIX/Unix (ps -ef) style options — don't mix them.
Most common invocations
ps aux (BSD style) and ps -ef (POSIX style) are the two standard ways to list every process on the system — use either; they show the same processes with slightly different column layouts. BSD-style adds %CPU and %MEM columns that are absent from the POSIX format.
ps aux # all processes, user-friendly (BSD)
ps -ef # all processes, full format (POSIX)
ps -eF # POSIX + extra columns
ps -ely # POSIX + long format (no truncation)
ps axjf # BSD + forest (process tree)
ps -ejH # POSIX + hierarchical tree
Output (ps aux):
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 168756 13200 ? Ss Apr23 0:04 /sbin/init
root 423 0.0 0.1 47380 8916 ? Ss Apr23 0:00 /lib/systemd/systemd-journald
root 512 0.0 0.0 22200 4096 ? Ss Apr23 0:00 /lib/systemd/systemd-udevd
www-data 1234 0.0 0.5 12340 5120 ? Ss 09:00 0:00 nginx: master process
www-data 1235 0.0 0.3 9876 3456 ? S 09:00 0:00 nginx: worker process
alice 8821 0.0 0.2 16788 4200 pts/0 Ss 10:14 0:00 bash
alice 9103 0.0 0.1 11212 3040 pts/0 R+ 10:22 0:00 ps aux
Output (ps -ef):
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 Apr23 ? 00:00:04 /sbin/init
root 423 1 0 Apr23 ? 00:00:00 /lib/systemd/systemd-journald
www-data 1234 1 0 09:00 ? 00:00:00 nginx: master process
www-data 1235 1234 0 09:00 ? 00:00:00 nginx: worker process
alice 8821 8800 0 10:14 pts/0 00:00:00 bash
alice 9110 8821 0 10:22 pts/0 00:00:00 ps -ef
Column reference (BSD aux)
| Column | Meaning |
|---|---|
USER | Process owner |
PID | Process ID |
%CPU | CPU usage % |
%MEM | Memory usage % |
VSZ | Virtual memory size (KB) |
RSS | Resident (physical) memory (KB) |
TTY | Controlling terminal |
STAT | Process state |
START | Start time |
TIME | Cumulative CPU time |
COMMAND | Command + arguments |
Process states (STAT)
| Code | Meaning |
|---|---|
R | Running or runnable |
S | Sleeping (interruptible) |
D | Uninterruptible sleep (I/O) |
T | Stopped (signal or debugger) |
Z | Zombie (finished, not reaped) |
< | High priority |
N | Low priority |
L | Locked pages in memory |
s | Session leader |
l | Multi-threaded |
+ | Foreground process group |
Filtering and searching
Pipe ps output through grep for quick name searches, or use pgrep which searches process names directly and returns just the PIDs — cleaner for scripting and avoids the classic grep self-inclusion problem.
ps aux | grep nginx # find by name
ps aux | grep -v grep | grep nginx # exclude grep itself
ps -C nginx # by command name (POSIX)
ps -p 1234 # by PID
ps -p 1234,5678 # multiple PIDs
ps -u alice # by user
ps --ppid 1234 # children of PID
# Find PID of a program
pgrep nginx # print matching PIDs
pgrep -l nginx # with process names
pgrep -a nginx # with full command
pgrep -u root nginx # by user + name
Output (ps aux | grep -v grep | grep nginx):
www-data 1234 0.0 0.5 12340 5120 ? Ss 09:00 0:00 nginx: master process
www-data 1235 0.0 0.3 9876 3456 ? S 09:00 0:00 nginx: worker process
www-data 1236 0.0 0.3 9876 3456 ? S 09:00 0:00 nginx: worker process
Output (pgrep nginx):
1234
1235
1236
Output (pgrep -l nginx):
1234 nginx
1235 nginx
1236 nginx
Custom output format
-o (or -eo to select all processes) lets you specify exactly which columns appear in the output and in what order, using comma-separated specifier names. Combine with --sort=-COLUMN to rank processes by CPU or memory without needing a separate sort call.
ps -eo pid,ppid,user,%cpu,%mem,vsz,rss,stat,comm
ps -eo pid,user,comm --sort=-%cpu | head -15 # top CPU consumers
ps -eo pid,user,comm --sort=-%mem | head -15 # top memory consumers
ps -eo pid,etime,comm # elapsed time since start
ps -eo pid,lstart,comm # absolute start time
# All threads of a process
ps -T -p 1234
ps -eLf | grep 1234
Output (ps -eo pid,user,comm --sort=-%cpu | head -15):
PID USER COMMAND
4521 alice python3
1234 www-data nginx
8800 root sshd
9200 bob node
423 root systemd-journal
Process tree
A process tree view shows parent-child relationships between processes, making it easy to identify which daemon spawned a misbehaving worker. ps axjf produces an ASCII art tree inline; pstree is a dedicated tool with cleaner output and options to show PIDs and usernames.
ps axjf # ASCII art tree (BSD)
ps -ejH # indented tree (POSIX)
pstree # dedicated tool
pstree -p # with PIDs
pstree -u # with usernames
pstree -a # with arguments
Output (pstree -p):
systemd(1)─┬─sshd(800)───sshd(8800)───bash(8821)───pstree(9210)
├─nginx(1234)─┬─nginx(1235)
│ └─nginx(1236)
├─systemd-journal(423)
└─cron(611)
Watch processes
watch reruns a command at a fixed interval and updates the display in place, giving a lightweight live view of process state without the overhead of top. Useful for monitoring a specific workload without switching to an interactive tool.
watch -n 1 'ps aux --sort=-%cpu | head -20'
Output: (none — exits 0 on success)
ss — Socket Statistics (modern netstat)
ss is the modern replacement for netstat — faster, more features, same basic concepts.
Common invocations
ss -tuln is the go-to command: it shows all TCP and UDP listening sockets in numeric form (no slow DNS lookups). Add -p to see which process owns each socket — this requires root to see sockets owned by other users.
ss -tuln # TCP+UDP listening sockets, numeric (most common)
ss -tulnp # + show process name/PID
ss -ta # all TCP connections
ss -ua # all UDP sockets
ss -x # UNIX domain sockets
ss -s # summary statistics
ss -i # TCP internal info (window, RTT)
Output (ss -tulnp):
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
tcp LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=800,fd=3))
tcp LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=1234,fd=6))
tcp LISTEN 0 511 0.0.0.0:443 0.0.0.0:* users:(("nginx",pid=1234,fd=8))
tcp LISTEN 0 128 127.0.0.1:5432 0.0.0.0:* users:(("postgres",pid=2100,fd=5))
udp UNCONN 0 0 0.0.0.0:68 0.0.0.0:* users:(("dhclient",pid=345,fd=6))
Output (ss -s):
Total: 142
TCP: 38 (estab 24, closed 8, orphaned 0, timewait 8)
Transport Total IP IPv6
RAW 0 0 0
UDP 6 4 2
TCP 30 22 8
INET 36 26 10
FRAG 0 0 0
Option flags
| Flag | Meaning |
|---|---|
-t | TCP sockets |
-u | UDP sockets |
-x | UNIX domain sockets |
-l | Listening sockets only |
-a | All sockets (listening + connected) |
-n | Numeric (no DNS/service name resolution) |
-p | Show process (requires root for others' processes) |
-e | Extended info (UID, inode) |
-o | Show timer info |
-r | Resolve hostnames |
-4 | IPv4 only |
-6 | IPv6 only |
Filtering with expressions
ss accepts a filter expression after the flags to match by TCP state, source address, destination port, or remote host — all evaluated by the kernel before results are returned, which is much faster than piping to grep on a system with thousands of connections.
ss -tnp state established # established TCP
ss -tnp state time-wait # TIME-WAIT
ss -tnp state listening # listening
ss -tnp '( dport = :80 or sport = :80 )' # port 80
ss -tnp dst 10.0.0.1 # connections to host
ss -tnp src :22 # connections from port 22
ss -tnp dport :\> :1024 # dest port > 1024
Output: (none — exits 0 on success)
TCP states
ESTABLISHED, SYN-SENT, SYN-RECV, FIN-WAIT-1, FIN-WAIT-2, TIME-WAIT, CLOSE, CLOSE-WAIT, LAST-ACK, LISTEN, CLOSING
ss -t state established '( dport = :443 )' # HTTPS connections
ss -tan | awk '{print $1}' | sort | uniq -c | sort -rn # count by state
Output (ss -tan | awk '{print $1}' | sort | uniq -c | sort -rn):
24 ESTABLISHED
8 TIME-WAIT
4 LISTEN
2 CLOSE-WAIT
What's listening on a port
When a port is unexpectedly in use or a service fails to bind, use ss -tlnp | grep :PORT to identify the process holding it. Combine with sudo if the socket belongs to another user — without it, the process column will be empty for sockets you don't own.
ss -tlnp | grep :8080
ss -tnlp sport = :443
sudo ss -tlnp | grep '"nginx"' # filter by process name
Output (ss -tlnp | grep :8080):
tcp LISTEN 0 128 0.0.0.0:8080 0.0.0.0:* users:(("node",pid=9200,fd=19))
netstat — Legacy (still common)
netstat is deprecated on Linux (replaced by ss) and is no longer installed by default on current Debian, Ubuntu, Fedora, RHEL, or Arch — you typically have to apt install net-tools (or equivalent) to get it back. It remains the standard tool on macOS and BSD, where the iproute2 ss replacement does not exist.
netstat -tuln # TCP+UDP listening, numeric (like ss -tuln)
netstat -tunap # all TCP/UDP + process info
netstat -rn # routing table
netstat -i # interface statistics
netstat -s # summary by protocol
netstat -an # all connections, numeric
Output (netstat -tuln):
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:443 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:5432 0.0.0.0:* LISTEN
udp 0 0 0.0.0.0:68 0.0.0.0:*
macOS / BSD netstat differences
# macOS: -p specifies protocol, not process
netstat -anp tcp # macOS: all TCP (not process info)
netstat -anp udp # macOS: all UDP
# Get process info on macOS
sudo lsof -i :8080 # what's on port 8080
sudo lsof -i tcp # all TCP connections
Output: (none — exits 0 on success)
lsof — List Open Files / Ports
lsof (list open files) shows every file descriptor a process has open — including regular files, sockets, pipes, and devices. On Linux, "everything is a file," so lsof -i is an effective alternative to ss for correlating network connections with specific processes, and lsof +D /path reveals which processes hold files open in a directory.
lsof -i # all network connections
lsof -i :8080 # what's using port 8080
lsof -i tcp # all TCP connections
lsof -i tcp:443 # port 443 specifically
lsof -i @192.168.1.1 # connections to/from IP
lsof -p 1234 # files opened by PID 1234
lsof -u alice # files opened by user
lsof -c nginx # files opened by nginx processes
lsof +D /var/log # files open in /var/log tree
Output (lsof -i :80):
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nginx 1234 www-data 6u IPv4 23456 0t0 TCP *:http (LISTEN)
nginx 1235 www-data 6u IPv4 23456 0t0 TCP *:http (LISTEN)
nginx 1236 www-data 6u IPv4 23456 0t0 TCP *:http (LISTEN)
Practical recipes
# What process is listening on port 80?
sudo ss -tlnp | grep ':80 '
sudo lsof -i :80
# All established connections and their process
sudo ss -tnp state established
Output (sudo ss -tnp state established):
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
ESTAB 0 0 10.0.0.10:22 10.0.0.5:52314 users:(("sshd",pid=8800,fd=4))
ESTAB 0 0 10.0.0.10:443 203.0.113.42:49182 users:(("nginx",pid=1235,fd=12))
ESTAB 0 0 10.0.0.10:443 203.0.113.99:51033 users:(("nginx",pid=1236,fd=14))
# Count connections per remote IP
ss -tn state established | awk '{print $5}' | cut -d: -f1 | \
sort | uniq -c | sort -rn | head -20
Output:
42 203.0.113.42
17 10.0.0.5
8 192.168.1.101
3 198.51.100.7
# Find zombie processes
ps aux | awk '$8=="Z" {print $2, $11}'
Output:
14321 defunct-worker
14989 old-handler
# Kill all processes matching name (safer than killall)
pgrep -x stale-worker | xargs kill -TERM
# Top 10 memory-hungry processes
ps aux --sort=-%mem | awk 'NR<=11 {printf "%-10s %5s %5s %s\n",$1,$3,$4,$11}'
Output:
USER %CPU %MEM COMMAND
alice 1.2 4.8 java
www-data 0.0 1.3 nginx
postgres 0.0 0.9 postgres
bob 0.3 0.7 node
root 0.0 0.4 python3
# Watch connection count to port 443
watch -n 2 'ss -tn state established dst :443 | wc -l'
# Find processes with open network connections
sudo lsof -i -n -P | grep ESTABLISHED | awk '{print $1,$9}' | sort -u
Output:
chrome 203.0.113.42:443->10.0.0.10:55210
nginx 10.0.0.10:443->203.0.113.42:49182
sshd 10.0.0.10:22->10.0.0.5:52314
On modern Linux, always prefer
ssovernetstat.ssreads directly from the kernel's netlink socket rather than walking/proc/net/*, making it significantly faster on systems with many connections. As of 2026,netstatis no longer installed by default on Debian, Ubuntu, Fedora, RHEL, or Arch — install thenet-toolspackage if you need it.