Networking

Find which process is listening on a port

List every port a server listens on, read which addresses it accepts connections on, and trace a port back to its process and its systemd unit, including ports systemd holds itself.

Written againstDebian 12Ubuntu 24.04RHEL 9iproute2 6.1+lsof 4.94+
Reading time
4 min
Commands to copy
11
Last checked
24 Oct 2026

A service accepts connections on a port only while a process holds a listening socket for it. ss, from iproute2, lists those sockets with the process behind each one. It replaces netstat, which Debian 12, Ubuntu 24.04, and RHEL 9 no longer install by default.

List the listening sockets

shell
$ sudo ss -tlnpState  Recv-Q Send-Q Local Address:Port  Peer Address:Port ProcessLISTEN 0      511          0.0.0.0:80         0.0.0.0:*     users:(("nginx",pid=1204,fd=6),("nginx",pid=1203,fd=6))LISTEN 0      128          0.0.0.0:22         0.0.0.0:*     users:(("sshd",pid=812,fd=3))LISTEN 0      244        127.0.0.1:5432       0.0.0.0:*     users:(("postgres",pid=990,fd=6))LISTEN 0      511             [::]:80            [::]:*     users:(("nginx",pid=1204,fd=7),("nginx",pid=1203,fd=7))LISTEN 0      128             [::]:22            [::]:*     users:(("sshd",pid=812,fd=4))

-t shows TCP, -l only listening sockets, -n numbers instead of names, and -p the process. Run it with sudo: without root, the Process column stays empty for every socket that belongs to another user. For UDP, use -u instead of -t. UDP has no listening state, so its sockets show as UNCONN.

shell
$ sudo ss -ulnpState  Recv-Q Send-Q    Local Address:Port Peer Address:Port ProcessUNCONN 0      0            127.0.0.54:53        0.0.0.0:*     users:(("systemd-resolve",pid=402,fd=20))UNCONN 0      0         127.0.0.53%lo:53        0.0.0.0:*     users:(("systemd-resolve",pid=402,fd=18))

For a listening TCP socket, Send-Q is the length of the queue of connections waiting to be accepted, and Recv-Q is how many are in it. A Recv-Q that stays near Send-Q means the program accepts connections more slowly than they arrive. The process name is cut to 15 characters, which is why systemd-resolved shows as systemd-resolve.

Read the address column

Local addressWho can connect
0.0.0.0:80Any IPv4 address of the machine, so anyone who can reach it
[::]:80Any IPv6 address of the machine
*:80Any address, IPv4 and IPv6, through one dual-stack socket
127.0.0.1:5432Only programs on the same machine. [::1] is the same for IPv6.
10.0.0.5:8080Only connections to that one address, here the private network
127.0.0.53%lo:53That address, and only on the interface after the % sign

In the first listing, PostgreSQL listens on 127.0.0.1 only, so other machines can’t reach it however the firewall is set. That’s its default; listen_addresses in postgresql.conf changes it.

Look up a single port

ss takes a filter after the options. Quote it, so the shell leaves it alone:

shell
$ sudo ss -tlnp 'sport = :443'State  Recv-Q Send-Q Local Address:Port Peer Address:Port ProcessLISTEN 0      511          0.0.0.0:443       0.0.0.0:*     users:(("nginx",pid=1204,fd=8),("nginx",pid=1203,fd=8))

Two PIDs on one socket is normal for servers with a main process and workers: nginx’s main process opens the socket and its workers inherit it. lsof and fuser give the same answer, with the user each process runs as. lsof isn’t in a minimal install; it’s the lsof package on all three distributions.

shell
$ sudo lsof -nP -iTCP:443 -sTCP:LISTENCOMMAND  PID     USER   FD   TYPE DEVICE SIZE/OFF NODE NAMEnginx   1203     root    8u  IPv4  21843      0t0  TCP *:443 (LISTEN)nginx   1204 www-data    8u  IPv4  21843      0t0  TCP *:443 (LISTEN)$ sudo fuser 443/tcp443/tcp:              1203  1204

Find the service behind the process

A PID tells you the program, not which unit started it or how to restart it. Ask systemd:

shell
$ ps -o unit= -p 1204nginx.service$ systemctl status 1204

systemctl status with a PID shows the unit that process belongs to, with its state and the last lines of its log. A process that isn’t in any service unit shows a session scope such as session-4.scope: someone started it from a shell, and no unit restarts it if it stops.

When the port is already in use

A service that can’t open its port fails at start with an error like this one in its journal:

shell
$ journalctl -u nginx -n 5nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)$ sudo ss -tlnp 'sport = :80'State  Recv-Q Send-Q Local Address:Port Peer Address:Port ProcessLISTEN 0      511                *:80              *:*     users:(("apache2",pid=2290,fd=4),("apache2",pid=2288,fd=4))

Here Apache, installed as a dependency of something else, took port 80 first. Stop and disable the one you don’t want (sudo systemctl disable --now apache2), or move one of them to another port. Two programs can only share a port when each binds a different address, for example one on 10.0.0.5:80 and one on 127.0.0.1:80.

/etc/systemd/resolved.conf.d/no-stub.conf
[Resolve]DNSStubListener=no

Ports held by systemd

With socket activation, systemd opens the port itself and starts the service on the first connection. ss then shows systemd with PID 1 as the owner, at least until the service has started. Ubuntu 24.04 runs OpenSSH this way, through ssh.socket.

shell
$ sudo ss -tlnp 'sport = :22'State  Recv-Q Send-Q Local Address:Port Peer Address:Port ProcessLISTEN 0      4096               *:22              *:*     users:(("systemd",pid=1,fd=60))$ systemctl list-sockets

systemctl list-sockets lists every socket systemd holds, with the unit that holds it and the service it starts. To free such a port, stop and disable the .socket unit. Stopping only the service isn’t enough, because systemd starts it again on the next connection.

Listening isn’t the same as reachable

A socket on 0.0.0.0 accepts connections from anywhere the packets can arrive from, but a firewall on the machine or in front of it decides whether they arrive. To check that from the other side, see Test whether a TCP port is reachable.

Last checked on 24 October 2026 against Debian 12, Ubuntu 24.04, RHEL 9, iproute2 6.1+, and lsof 4.94+. If a step doesn’t match what you see, contact us with the command and what it printed.