Finding which process is holding a port, and why kill does not always free it
bind: address already in use is one of the most common errors in operations
and one of the most frequently misdiagnosed. The port is held by something,
and the useful work is finding out what, then understanding why it is still
there.
Start with the listening sockets
ss is the modern replacement for netstat and shows what is listening, along
with the process that owns each socket:
sudo ss -tulpn
The flags are worth reading one by one, because the right combination saves a
lot of guessing: -t for TCP, -u for UDP, -l for listening sockets only,
-p to show the owning process, and -n to print numbers instead of resolving
service names and hostnames. Dropping -n makes the command slow on a host with
slow DNS, and the names it prints are rarely more useful than the port number.
To answer the specific question — who has port 8080 — filter on the port:
sudo ss -tulpn | grep ':8080'
The -p flag requires root to see processes you do not own. Run it without
sudo and sockets owned by other users show an empty process column, which
looks like the socket has no owner. That misreading sends people looking for a
phantom process that is in fact running as another user.
Confirm what a socket is actually bound to
A service bound to 127.0.0.1:8080 and one bound to 0.0.0.0:8080 look similar
in a quick scan but behave completely differently: the first is reachable only
from the machine itself, the second from anywhere. The local address column tells
you which:
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=712,fd=3))
tcp LISTEN 0 511 127.0.0.1:8080 0.0.0.0:* users:(("node",pid=4411,fd=19))
The second line is a service that cannot be reached from outside the host, which is a common cause of "it works locally but not remotely" that has nothing to do with a firewall.
When ss shows nothing and the port is still in use
This is the case that wastes the most time. ss lists no listener on the port,
yet binding fails. The usual cause is a socket in TIME_WAIT, which is a normal
part of TCP teardown: the side that closed first keeps the connection record for
a period (twice the maximum segment lifetime) so late packets are not
misinterpreted.
Sockets in this state do not appear in a listening-only listing:
ss -tan | grep ':8080'
If the output shows TIME_WAIT entries and no LISTEN, the port is not held by
a process at all — it is being held by the kernel on behalf of a closed
connection. Nothing needs to be killed. It clears itself, typically within a
minute.
There is a legitimate fix for a server that needs to rebind immediately during
development, and it belongs in the application, not in a sysctl change:
# setsockopt(SO_REUSEADDR) lets a new socket bind while old
# connections are still in TIME_WAIT. Most frameworks set this already.
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
SO_REUSEADDR is why a well-behaved server restarts cleanly while a hand-rolled
one does not. It is worth checking whether your framework sets it before reaching
for anything more drastic.
A process that will not die
When the owner is a real process and kill does not remove it, the sequence is
worth following in order rather than jumping to -9.
First, send the default SIGTERM, which asks the process to shut down cleanly:
kill 4411
Then confirm whether it actually exited, rather than assuming:
ps -p 4411 -o pid,stat,etime,cmd
A process in state D (uninterruptible sleep) is blocked in a kernel call,
usually disk I/O on a failing device or a stuck network filesystem. SIGKILL
cannot interrupt D state, because the signal is delivered when the process
returns to userspace and it never does. In that case the only options are to fix
the underlying I/O or reboot. Killing harder will not help, and it is worth
recognising the state before spending time on it.
If the process is in a normal state and ignored SIGTERM, escalate:
kill -9 4411
The trap: the parent restarts the child
A process that reappears with a new PID seconds after being killed is not failing to die. It is being respawned, and the thing to look at is its parent:
ps -o pid,ppid,cmd -p 4411
Then look at what the parent is:
ps -p <PPID> -o pid,cmd
If the parent is systemd, the unit is configured to restart and the correct action is to stop the unit rather than the process:
sudo systemctl stop myapp.service
If the parent is a supervisor like supervisord or a container runtime, the
same logic applies — stop the supervisor's view of the service, or it will
simply start the process again. Killing the child repeatedly is the loop that
makes people conclude the process is "unkillable".
To find the unit that owns a process directly, when you know the PID:
systemctl status 4411
systemctl status accepts a PID and prints the unit it belongs to, which
short-circuits the parent-chasing above.
Check the container case separately
If the port is held inside a container, the host's ss may show the published
port but the owning process is in another namespace:
docker ps --format '{{.Names}} {{.Ports}}' | grep 8080
The fix is to stop or remove the container, not to hunt for a host process. On
systems where you do not have Docker socket access, ss will show the port as
bound by docker-proxy or by the container runtime, which is the tell that the
real owner is inside a container.
The short sequence worth memorising
sudo ss -tulpn | grep ':PORT' # is something listening, and who
ss -tan | grep ':PORT' # is it TIME_WAIT instead
ps -o pid,ppid,cmd -p PID # who started it, if it keeps coming back
systemctl status PID # which unit owns it
Work through those four in order and the port is either free or you know exactly what is holding it and why.