Find Which Process Is Listening on a Port in Linux
"Address already in use." At some point, every service you start on Linux fails with that message. The next question is always the same: what is already using that port? This post covers the three tools I use (ss, lsof and fuser). It also covers reading /proc/net/tcp directly when none of them is installed. Finally, it explains why all of them sometimes show you a port but not its owner.
To have something to find, I started a small web server bound to the loopback address on port 8731:
$ python3 -m http.server 8731 --bind 127.0.0.1 &All output below is from an EL8 server with iproute2 6.2 and lsof 4.93, run as an ordinary user. Your PIDs and port lists will differ.
ss: the modern default
ss comes with iproute2 and replaces the old netstat, which many minimal installs no longer include. The flags I type almost every day are -ltnp:
-l: listening sockets only-t: TCP (use-ufor UDP, or both)-n: numeric ports and addresses, no DNS or/etc/serviceslookups-p: show the process that owns each socket
$ ss -ltnp
State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
LISTEN 0 65536 127.3.2.1:3306 0.0.0.0:*
LISTEN 0 128 0.0.0.0:111 0.0.0.0:*
LISTEN 0 65536 127.3.2.1:80 0.0.0.0:*
LISTEN 0 65536 127.3.2.1:8025 0.0.0.0:*
LISTEN 0 5 127.0.0.1:8731 0.0.0.0:* users:(("python3",pid=152425,fd=3))
LISTEN 0 511 127.0.0.1:8091 0.0.0.0:* users:(("node",pid=120638,fd=18))
LISTEN 0 65536 127.3.2.1:443 0.0.0.0:*
LISTEN 0 128 [::]:111 [::]:*The Python server is there, with its command name, PID and file descriptor. On a listening socket, Send-Q is the accept backlog, and 5 is also the default queue size of Python's socketserver. The Local Address column tells you where it listens: 127.0.0.1 is reachable only from this machine, while 0.0.0.0 or [::] means every interface. That column has answered more "I cannot connect from outside" questions for me than any firewall rule.
Rather than grep, you can give ss a filter expression:
$ ss -ltnp 'sport = :8731'
State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
LISTEN 0 5 127.0.0.1:8731 0.0.0.0:* users:(("python3",pid=152425,fd=3))Quote the filter so the shell does not change it. grep ':8731' works too, but it also matches 18731 and any peer address that contains the string.
Why most of the ports have no process
Look again at the full list: ports 3306, 80, 443 and others have an empty process column. They do not lack an owner. They belong to processes running as other users (root, here), and an unprivileged user is not allowed to look inside those processes.
The reason is how these tools work. The kernel's socket table records the local and remote address, the state, the owning UID and an inode number, but not a PID. To map a socket to a process, ss, lsof and fuser search /proc/<pid>/fd/ for every process, looking for a file descriptor that points at that socket inode. Those fd directories are readable only by the process owner (and root). So as a normal user you see every socket on the system, but only your own processes behind them.
The fix is simply to run the command with root privileges: sudo ss -ltnp. If you see a port with no owner, that is almost always the reason, not a bug.
lsof: when you think in files
lsof lists open files, and on Linux a socket is a file. -i :PORT selects internet sockets on that port:
$ lsof -i :8731
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
python3 152425 nawawi 3u IPv4 338786 0t0 TCP localhost:8731 (LISTEN)By default lsof resolves names, so the address became localhost and well-known ports would show as http or mysql. On a busy server those lookups are slow. I usually add -n (no host names) and -P (no port names). I also restrict the output to listening TCP sockets, because -i :8731 on its own also matches every client connection to or from that port:
$ lsof -nP -iTCP:8731 -sTCP:LISTEN
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
python3 152425 nawawi 3u IPv4 338786 0t0 TCP 127.0.0.1:8731 (LISTEN)The NODE column, 338786, is the socket inode mentioned above. For scripts, -t prints only PIDs, one per line, with no header:
$ lsof -t -i :8731
152425The same permission rule applies, but lsof is quieter about it. Asking for a port owned by root prints nothing at all and exits with status 1, exactly as if nothing were listening:
$ lsof -i :3306; echo "rc=$?"
rc=1That is the danger: ss showed port 3306 as listening, but lsof says nothing is there. Without root, an empty lsof result proves nothing.
fuser: the shortest answer
fuser, from the psmisc package, takes a port/protocol argument and prints the PIDs using it:
$ fuser 8731/tcp
8731/tcp: 152425
$ fuser -v 8731/tcp
USER PID ACCESS COMMAND
8731/tcp: nawawi 152425 F.... python3The 8731/tcp: label goes to standard error and the PID to standard output, so pid=$(fuser 8731/tcp 2>/dev/null) captures just the number (with leading spaces). fuser -k 8731/tcp sends SIGKILL to everything using the port. It is useful, but check with -v first. Killing the wrong process with -9 skips its cleanup handlers.
Reading /proc/net/tcp when no tools are available
Containers and rescue systems often have none of these tools. The kernel still exposes the socket table in /proc/net/tcp (and /proc/net/tcp6 for IPv6), and you can read it with nothing more than grep. Addresses and ports are in hexadecimal, so first convert the port:
$ printf '%04X\n' 8731
221B
$ grep ':221B ' /proc/net/tcp
0: 0100007F:221B 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 338786 1 0000000000000000 100 0 0 10 0Reading the fields left to right: 0100007F:221B is the local address and port (the IPv4 address is stored in host byte order, so on x86 it reads backwards: 7F.00.00.01 is 127.0.0.1). The state 0A is LISTEN. 1000 is the UID of the owner. And 338786 is the socket inode. A short awk program turns the whole table into something readable (strtonum needs GNU awk):
awk 'NR>1 && $4=="0A" {
split($2, a, ":")
printf "%d uid=%s inode=%s\n", strtonum("0x" a[2]), $8, $10
}' /proc/net/tcp8731 uid=1000 inode=338786
8091 uid=1000 inode=220857
111 uid=0 inode=17271
3306 uid=0 inode=62463
443 uid=0 inode=68017
80 uid=0 inode=63479
8025 uid=0 inode=67793Note that the UID is visible even for root's sockets, which already narrows the search. To get from the inode to a PID, do what ss -p does internally and search the file descriptor links:
$ find /proc/[0-9]*/fd -lname 'socket:\[338786\]' 2>/dev/null
/proc/152425/fd/3PID 152425, the same answer as the other tools. Run the same search for port 3306's inode as a normal user and it finds nothing, for the same permission reason. As root it works.
Quick reference
| Task | Command |
|---|---|
| All listening TCP ports with owners | sudo ss -ltnp |
| One port, filtered | sudo ss -ltnp 'sport = :8731' |
| UDP too | sudo ss -lutnp |
| Listener on a port via lsof | sudo lsof -nP -iTCP:8731 -sTCP:LISTEN |
| PID only, for scripts | sudo lsof -t -i :8731 |
| Shortest | sudo fuser -v 8731/tcp |
| No tools installed | grep ':221B ' /proc/net/tcp |
Once you have the PID, ps -o user,pid,args -p PID shows the full command line, which usually tells you which service or container it belongs to. If you want to wait in a script until a port is free, __pgrepf() shows a pure-Bash way of looking up processes by name.