Originally published on DevToolHub.

"Address already in use" tells you a port is taken. It doesn't tell you by what. Finding the actual process bound to a port on Linux is a three-command workflow, not one magic incantation — and two of those three commands have a syntax gotcha that produces wrong or misleading output if you get it backwards.

Step 1: Confirm the port is actually bound, with ss

ss -tlnp is the fastest way to see what's listening. -t restricts output to TCP sockets, -l shows only listening sockets, -n skips reverse-DNS lookups so it returns instantly, and -p shows the owning process — in practice you need sudo to see details for other users' processes.

ss -tlnp | grep :8080 LISTEN 0 128 0.0.0.0:8080 0.0.0.0:* users:(("node",pid=48213,fd=22)) 

That already gives you the PID and process name. ss only covers network sockets, though — it won't show a stale listener's other open file handles.

Step 2: Confirm the exact process, with lsof

lsof -i :8080 answers the same question from the file-descriptor side. lsof's -i accepts [46][protocol][@hostname|hostaddr][:service|port], so -i :8080 means any protocol/host on port 8080, while -i tcp:8080 narrows to TCP only.

lsof -i :8080 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME node 48213 deploy 22u IPv4 812933 0t0 TCP *:8080 (LISTEN) 

The FD column matters if you're also chasing a "too many open files" error — lsof shows every open file and socket for that PID.

Step 3: Inspect the process before you kill it

ps -p 48213 -o pid,ppid,user,cmd,%cpu,%mem 

The gotcha: ps aux and ps -aux are not the same command. Per the ps man page, ps -aux under POSIX rules means "processes with a terminal plus processes owned by a user named x," and it only behaves like ps aux because ps falls back to that when no user x exists. Stick to ps aux for the traditional listing, and ps -p PID -o ... once you have a specific PID.

If you're hunting the heaviest process by resource use instead, ps aux --sort=-%cpu | head sorts descending by CPU — and per the man page, %cpu "will not add up to 100% unless you are lucky," since it's a lifetime ratio, not a live snapshot like top produces.

Killing it safely

kill -15 48213 

-15 sends SIGTERM, a clean-shutdown request. Only escalate to kill -9 48213 (SIGKILL) if the process is still alive a few seconds later — SIGKILL skips cleanup and can leave sockets or lock files behind.

Quick Summary:

  • ss -tlnp | grep :PORT finds the PID fastest, with -n avoiding slow DNS lookups
  • lsof -i :PORT cross-checks from the file-descriptor side and shows every open file for that PID
  • ps -aux means "processes owned by user x" under POSIX and only falls back to ps aux — type ps aux without the dash
  • %cpu is a lifetime ratio and won't sum to 100% across processes
  • Send SIGTERM (kill -15) before SIGKILL (kill -9)

Full workflow at DevToolHub.