Short answer: sudo netstat -tulpn lists every TCP and UDP port a program listens on, with the PID and name of that program. On current Linux distributions netstat is often not installed, because it belongs to the old net-tools package; sudo ss -tulpn gives the same answer with the same letters and ships with every mainstream distribution. On Windows, netstat -ano lists every connection and listening port with the owning process ID in the last column.
sudo netstat -tulpn # net-tools
sudo ss -tulpn # iproute2: the same question, preinstallednetstat -anoThe ss commands below were run on Debian 12 with iproute2 6.1. That server has no netstat at all (command -v netstat prints nothing), so the netstat flags follow the net-tools netstat(8) man page, and the Windows ones Microsoft’s netstat reference.
netstat to ss: the translation table
The netstat man page itself says the program is mostly obsolete and names the replacements: ss for sockets, ip route for netstat -r, ip -s link for netstat -i and ip maddr for netstat -g. Most socket flags carry over letter for letter:
| Task | netstat (net-tools) | Replacement (iproute2) |
|---|---|---|
| Listening TCP and UDP ports, with process | netstat -tulpn | ss -tulpn |
| Listening TCP ports only | netstat -tlnp | ss -tlnp |
| Every TCP socket, any state | netstat -tan | ss -tan |
| Connections, without listening sockets | netstat -tn | ss -tn |
| Timers (keepalive, retransmit) | netstat -tno | ss -tno |
| Owner and inode of each socket | netstat -tne | ss -tne |
| Unix domain sockets | netstat -x | ss -x |
| Summary counts | netstat -s | ss -s, or nstat -az for every counter |
| Routing table | netstat -rn | ip route |
| Interface counters | netstat -i | ip -s link |
| Multicast groups | netstat -g | ip maddr |
| Refresh every second | netstat -tanc | watch -n 1 ss -tan |
One difference catches people out: netstat -s prints hundreds of protocol counters, ss -s only a short summary of socket counts. For the counters, nstat -az (also in iproute2) is the match; without -a it shows only what changed since its last run.
netstat -tulpn, letter by letter
-tTCP sockets,-uUDP sockets. Leave both out and netstat shows every family, Unix sockets included, which is a lot of noise.-llistening sockets only: the services waiting for connections, not the connections themselves.-ashows both.-pthe PID and program name. It needs root to name processes of other users; withoutsudothose rows show-.-nnumbers instead of names:0.0.0.0:22rather than0.0.0.0:ssh, and no slow reverse DNS lookups for remote addresses.
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 1021/sshd
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 1340/nginx: master
tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN 1188/mariadbd
tcp6 0 0 :::22 :::* LISTEN 1021/sshd
tcp6 0 0 :::80 :::* LISTEN 1340/nginx: master
udp 0 0 127.0.0.53:53 0.0.0.0:* 587/systemd-resolveThe column to read is Local Address. 0.0.0.0 and ::: mean every IPv4 or IPv6 address, so SSH and nginx above are reachable from the network unless a firewall stops them; 127.0.0.1:3306 means MariaDB accepts connections from the server itself only. UDP has no listening state, so its State column stays empty. The same listing from ss puts the process in a users:(("sshd",pid=1021,fd=3)) column instead; how to read it, and how to test from outside whether a port is really open, is in checking open ports on Linux. Which service normally sits on which number is in the list of common ports.
Is netstat deprecated? Installing it anyway
Yes, in practice. net-tools still builds and works, but its last release, 2.10, came out in 2021, and Debian, Ubuntu and Rocky images no longer install it by default. ss comes with iproute2, which every current distribution ships, and it asks the kernel for socket data directly instead of parsing the text files under /proc/net, which makes it noticeably faster on a server with tens of thousands of connections. If you prefer netstat, one package brings it back:
sudo apt install -y net-toolssudo dnf install -y net-toolsThe same package also restores ifconfig, route and arp; the details, and why ifconfig can still be “not found” afterwards, are in ifconfig: command not found.
Find what listens on a port
sudo ss -ltnp 'sport = :443' # ss filters by port itself
sudo netstat -tulpn | grep ':443 ' # netstat: note the space after 443Grep for :443 with a trailing space, or :443\b, because a bare :443 also matches ports 4430 and 44300. The ss filter has no such trap. Once you have the PID, systemctl status PID names the service it belongs to, so you know what to stop or reconfigure. If a program refuses to start because its port is taken, the next steps are in Address already in use.
Count connections per state
On a busy server the question is often not “what listens” but “how many connections, in which state”. Both tools print the state in a column, so a short pipeline counts them:
ss -tan | awk 'NR>1 {print $1}' | sort | uniq -c | sort -rn
netstat -tan | awk 'NR>2 {print $6}' | sort | uniq -c | sort -rn 212 ESTAB
96 TIME-WAIT
11 LISTEN
4 SYN-RECV
2 CLOSE-WAITThe ss line skips one header row, the netstat line two, because netstat prints a title above its column names. ss can also filter by state on its own, and then it leaves the State column out of the output. On iproute2 6.1 that makes the remote address the fourth field, which is what the per-address count below relies on:
ss -s # quick totals
ss -Htn state established | wc -l # established connections (-H: no header)
ss -Htn state established '( sport = :443 )' | wc -l # ...to your HTTPS port
ss -Htn state established | awk '{print $4}' | sed 's/:[0-9]*$//' |
sort | uniq -c | sort -rn | head # connections per remote addressWhat the states mean
| ss | netstat | Meaning | When to worry |
|---|---|---|---|
| LISTEN | LISTEN | A service waiting for connections | Only if it listens on an address it should not |
| ESTAB | ESTABLISHED | An open connection | A sudden jump from one address can be a flood or a broken client |
| TIME-WAIT | TIME_WAIT | Closed by this side, kept briefly so late packets are not mistaken for a new connection (60 seconds on Linux) | Rarely: thousands are normal behind a busy proxy |
| CLOSE-WAIT | CLOSE_WAIT | The other side closed; this program has not closed its socket yet | When the count only grows: the application leaks sockets |
| SYN-RECV | SYN_RECV | A handshake that has not completed | Hundreds at once suggest a SYN flood or a full backlog |
| SYN-SENT | SYN_SENT | This server is trying to connect out | If they pile up, the remote end or a firewall drops the packets |
| FIN-WAIT-1, FIN-WAIT-2, LAST-ACK | FIN_WAIT1, FIN_WAIT2, LAST_ACK | Connections in the middle of closing | Almost never |
CLOSE-WAIT is the one that points at a bug. The remote side is done and the kernel is waiting for your program to call close(), which never happens. Each of those sockets holds a file descriptor, and if the count keeps growing, the process ends up with too many open files. TIME-WAIT, by contrast, is TCP working as designed, and it is also why a restarted service can briefly fail to bind its port; the TIME_WAIT section of the bind error guide covers that case.
netstat -rn and netstat -i: routes and interfaces
netstat also prints the routing table and per-interface counters, two jobs that moved to the ip command. The same default route in both formats, with example addresses:
$ netstat -rn
Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt Iface
0.0.0.0 203.0.113.1 0.0.0.0 UG 0 0 0 eth0
203.0.113.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
$ ip route
default via 203.0.113.1 dev eth0 proto static
203.0.113.0/24 dev eth0 proto kernel scope link src 203.0.113.100.0.0.0 with flag G (gateway) is the default route, written default via by ip; U means the route is up. A server with no default route can reach its own subnet and nothing else. For interface counters, ip -s link replaces netstat -i: an errors count that keeps climbing between two runs points at the link or the driver.
netstat on Windows: netstat -ano
netstat -ano # all connections and ports, numeric, with PID
netstat -ano | findstr LISTENING # listening ports only
netstat -ano | findstr :443 # one port (also matches 4430, read the numbers)
tasklist /FI "PID eq 1052" # which program is PID 1052
netstat -abno # program names too (administrator prompt, slow)
netstat -ano 5 # repeat every 5 seconds until Ctrl+CActive Connections
Proto Local Address Foreign Address State PID
TCP 0.0.0.0:135 0.0.0.0:0 LISTENING 1012
TCP 0.0.0.0:3389 0.0.0.0:0 LISTENING 1052
TCP 203.0.113.10:3389 198.51.100.7:51234 ESTABLISHED 1052
TCP 203.0.113.10:50122 192.0.2.80:443 TIME_WAIT 0
UDP 0.0.0.0:3389 *:* 1052-a shows connections and listening ports, -n skips name lookups and -o adds the PID; there is no -p for processes as on Linux, because on Windows -p picks a protocol (netstat -ano -p tcp). -b names the executable behind each line, but only from an elevated prompt, and it can take a while. -r prints the routing table (the same as route print), -s per-protocol statistics and -e interface counters. PID 0 on a TIME_WAIT line is normal: the connection no longer belongs to any process.
PowerShell has the same data as objects, which is easier to filter and count:
Get-NetTCPConnection -State Listen | Sort-Object LocalPort |
Format-Table LocalAddress, LocalPort, OwningProcess
Get-Process -Id (Get-NetTCPConnection -LocalPort 443 -State Listen).OwningProcess
Get-NetTCPConnection | Group-Object State | Sort-Object Count -Descending |
Format-Table Count, NameOn a Windows server the usual next step after finding a listener is the firewall: opening a port in Windows Firewall shows how, and the RDP port guide walks through netstat -ano | findstr :3389 on a live RDP server.
netstat on macOS
macOS has the BSD netstat, where -p also means protocol and there is no process column in the usual output. netstat -an -p tcp lists TCP sockets and netstat -rn the routing table; for the program behind a port, use lsof, which macOS ships: sudo lsof -nP -iTCP -sTCP:LISTEN.
Common errors
| What you see | Cause | Fix |
|---|---|---|
netstat: command not found | net-tools is not installed | Use ss -tulpn, or sudo apt install net-tools |
(Not all processes could be identified, non-owned process info will not be shown, you would have to be root to see it all.) | netstat -p without root | Run it with sudo |
- in the PID/Program column | Another user's process without sudo, or a socket the kernel holds itself (WireGuard, NFS) | Run with sudo; a kernel socket has no process to show |
ss: command not found | A minimal image without iproute2, such as Alpine | apk add iproute2-ss, or BusyBox’s netstat -tulpn |
| A port you know is open does not appear | The service runs in a container with its own network namespace, and the host forwards the port with firewall rules instead of a listening process | Run ss inside the container’s namespace with nsenter, as in the ifconfig guide |
Frequently asked
What does netstat -tulpn do?
It lists listening sockets: -t TCP, -u UDP, -l listening only, -p the PID and program name, -n numeric addresses and ports. Run it with sudo, or the processes of other users show a dash instead of a name. On a system without net-tools, ss -tulpn shows the same information.
Is netstat deprecated on Linux?
In practice, yes. Its package, net-tools, is no longer installed by default on current Debian, Ubuntu and Rocky images, and the netstat man page calls the program mostly obsolete. ss replaces it for sockets, ip route for netstat -r and ip -s link for netstat -i. You can still install net-tools if you want netstat back.
How do I find which process uses a port on Windows?
Run netstat -ano | findstr :443 and read the PID in the last column, then tasklist /FI "PID eq 1234" to get the program name. In PowerShell, Get-NetTCPConnection -LocalPort 443 shows the PID in its OwningProcess column, and Get-Process -Id with that PID names the program. findstr :443 also matches ports such as 4430, so read the numbers.
What does CLOSE_WAIT mean in netstat?
The remote side closed the connection, but the local program has not closed its socket yet. A few are normal; a count that keeps growing means the application never closes those sockets, a leak that ends in a too many open files error.