Find What’s Using a Port on Linux (lsof, ss, ps)

terminal workflow diagram for using a port on linux, showing lsof and ss commands finding the process

“Address already in use” tells you a port is taken. It doesn’t tell you by what. Finding what’s using 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. This walks through the workflow in the order that actually works: confirm the port is bound, identify the exact process, then decide whether it’s safe to kill.

If you’re not sure which service normally owns a port number, our guide to default TCP and UDP ports is a quick lookup. To test the port from another machine once it’s free, netcat is the fastest check.

Step 1: Confirm the Port Is Actually Bound, With ss

ss -tlnp is the fastest way to see what’s listening. Broken down: -t restricts output to TCP sockets, -l shows only listening sockets instead of every connection, -n skips reverse-DNS and service-name resolution so it returns instantly instead of hanging on lookups, and -p shows the processes using each socket, per the ss man page. In practice you need sudo to see process details for sockets owned by other users.

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

That output already gives you the PID (48213) and the process name (node) without a second command. If ss is all you need, you’re done here — but ss only tells you about network sockets. It won’t tell you if that PID also has a dozen open file handles, or if the port is actually bound by a different process than the one you expect because of a stale listener.

Step 2: Confirm the Exact Process, With lsof

lsof -i :8080 gives you the same answer from the file-descriptor side, and it’s worth cross-checking when ss shows something unexpected. lsof‘s -i option accepts an address specification with several optional parts — [46][protocol][@hostname|hostaddr][:service|port] — so -i :8080 alone means “any protocol, any host, port 8080,” while -i tcp:8080 narrows it to TCP only, and -i @192.168.1.10:8080 would narrow it to a specific host, per the lsof man page.

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 here (22u) is the actual file descriptor number inside that process, which matters if you’re chasing a “too many open files” error alongside the port conflict — lsof is showing you every open file and socket for that PID, not just the network state.

Step 3: Inspect the Process Before You Kill It

Once you have a PID, don’t kill it blind — check what it actually is first, especially on a shared or production box.

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

⚠️ The gotcha: ps aux and ps -aux are not the same command. aux (no dash) is BSD-style syntax for “all processes, user-oriented format.” But per the ps man page, ps -aux under the POSIX standard means “all processes with a terminal, plus all processes owned by a user named x.” It only behaves like ps aux because ps falls back to that when no user called x exists. On a box where someone has created a user named x, it silently shows something else. The safe habit: ps aux for the traditional full listing, and ps -p PID -o ... when you already have a specific PID, which is exactly what step 3 above uses.

If you’re instead trying to find the heaviest process by resource use rather than by port, ps aux --sort=-%cpu | head sorts descending by CPU (the - prefix means descending), and %cpu‘s own definition is worth knowing before you trust it for capacity decisions: per the ps man page, “it will not add up to 100% unless you are lucky,” because it’s a snapshot ratio of process time to process lifetime, not a live instantaneous reading the way top produces.

Killing It Safely

Once you’ve confirmed the PID is what you think it is:

kill -15 48213

-15 sends SIGTERM, which asks the process to shut down cleanly — most well-behaved services flush connections and exit within a few seconds. Only escalate to kill -9 48213 (SIGKILL, which the process can’t intercept or ignore) if the process is still running after a few seconds, since SIGKILL skips the process’s own cleanup entirely and can leave lock files, PID files or half-written data behind.

Frequently Asked Questions

Q: Why does ss show a port as listening but lsof shows nothing for it?
A: Usually a permissions gap — run both as the same user (or with sudo), since a non-root user’s ss -p and lsof will only show full detail for processes that user owns.

Q: What’s the fastest single command to find what’s using a port?
A: ss -tlnp | grep :PORT for TCP, or sudo lsof -i :PORT if you need file-descriptor detail or don’t have ss available.

Q: Is ps -aux wrong?
A: It usually works, but only by accident. Under POSIX rules -aux means “processes with a terminal plus processes owned by a user named x“; ps falls back to ps aux only when no user x exists. Use ps aux (no dash) for the traditional listing.

Q: Why doesn’t %cpu in ps add up to 100% across all processes?
A: It’s calculated as a ratio of CPU time consumed to process lifetime, not a live snapshot the way top‘s CPU column is — the man page explicitly notes it won’t sum to 100% “unless you are lucky.”

Q: Should I always use SIGKILL to make sure a stuck process actually dies?
A: No — try SIGTERM (kill -15, or plain kill) first so the process can clean up open files and connections, and only reach for SIGKILL (kill -9) if it’s still alive a few seconds later.

Quick Summary:
– ss -tlnp | grep :PORT is the fastest way to find the PID bound to a port, with -n avoiding slow DNS lookups
– lsof -i :PORT cross-checks from the file-descriptor side and shows every open file for that PID, not just the socket
– ps -aux means “processes owned by a user named x” under POSIX and only falls back to ps aux when that user doesn’t exist — type ps aux without the dash
– %cpu in ps is a lifetime ratio, not a live reading, and won’t sum to 100% across processes
– Send SIGTERM (kill -15) before SIGKILL (kill -9) so the process can exit cleanly

Next time a service won’t start because “the address is already in use,” this is the workflow for using a port on Linux — run ss -tlnp | grep :PORT before you reach for kill -9 on a guess.

Related guides

Leave a Reply