Finding What Is Using a Port on Linux
Introduction¶
bind: address already in use — something already holds the port you want.
The fix is one command; the trick is knowing which, and handling the cases
where the obvious command comes up empty (containers, TIME_WAIT, namespaces).
The commands¶
ss — the modern default¶
# Every listening TCP/UDP socket, with the owning process
sudo ss -tulpn
# Just port 8080
sudo ss -tulpn 'sport = :8080'
# Who is CONNECTED to port 5432 right now (not just listening)
sudo ss -tnp 'dport = :5432 or sport = :5432'
Flags: t TCP, u UDP, l listening, p process, n numeric (don't resolve
names — much faster). The users:(("nginx",pid=1234,fd=6)) field is your
answer.
lsof — file-descriptor view¶
sudo lsof -i :8080
sudo lsof -iTCP -sTCP:LISTEN -P -n
lsof also shows which address it's bound to (127.0.0.1:8080 vs
*:8080) and the file descriptor number, which is handy when a process has the
port open but on a socket it forgot to close.
fuser — quick and blunt¶
sudo fuser 8080/tcp # prints the PID(s)
sudo fuser -k 8080/tcp # ...and kills them (careful)
The cases that come up empty¶
It's a container¶
ss on the host shows docker-proxy or nothing useful. The real listener is in
the container's namespace:
docker ps --filter publish=8080
docker inspect --format '{{.State.Pid}}' <container>
sudo nsenter -t <pid> -n ss -tulpn # run ss inside the container's netns
Or just docker port <container>.
It's in a network namespace (not a container)¶
ip netns list
sudo ip netns exec <ns> ss -tulpn
The port is in TIME_WAIT, not held by anything¶
After a server closes a connection, the socket sits in TIME_WAIT for ~60 s.
ss -tan state time-wait shows them. A new server can't bind unless it sets
SO_REUSEADDR — most do (net.ipv4.tcp_tw_reuse is for outbound). If your app
doesn't, it just has to wait, or you fix the app to set the socket option.
It's a raw/PF_PACKET socket¶
tcpdump, DHCP clients, and some VPNs use packet sockets that ss -tulpn
doesn't list. sudo ss -0 -p or sudo lsof -i more broadly.
systemd socket activation¶
The socket is held by systemd itself, not the service (the service starts on
first connection). systemctl list-sockets and look for a .socket unit on
that port.
Verification and troubleshooting¶
# After killing, confirm it's actually free
sudo ss -tulpn | grep ':8080' || echo "port is free"
# Bind is failing but ss shows nothing — check the exact address
sudo ss -tulpn | grep 8080
# app wants 0.0.0.0:8080 but something holds 127.0.0.1:8080 (or ::)
ssshows nothing, bind still fails — it's a namespace/container, an IPv6 vs IPv4 mismatch (::8080blocks0.0.0.0:8080whennet.ipv6.bindv6only=0), or you're not root and the owning process belongs to someone else —sudo.- Killed the process, port still busy for a minute —
TIME_WAITfrom its open connections. Normal; wait or useSO_REUSEADDR. - Process keeps coming back — it's under systemd/supervisor with
Restart=. Stop the unit, not the PID. fuser -kkilled too much —fuserkills every process with that port open, including clients. Preferss -tulpn→kill <specific pid>.
Related tools and reading¶
- On-site: Port Checker (from the outside), Linux commands reference.
- Related posts: The TCP three-way handshake, ss and conntrack for connection debugging.
Stuck on something this site can't fix?Reach out to Prabath directly on LinkedIn.More in Linux & Sysadmin
auditd Basics
auditd records who did what at the syscall level — file access, config changes, privilege use. Here's how to write useful rules without drowning the disk.
September 16, 2026SSH Server Hardening
The sshd_config settings that matter, in priority order — key-only auth first — plus how to apply them without locking yourself out of the box.
September 16, 2026