How to Kill a Process and Free Up a Port on Mac, Linux & Windows
You’ve started your local dev server and hit a dreaded error: “port 3000 is already in use.” It happens to every developer. Here’s the fastest way to find and kill the offending process on any operating system — macOS, Linux, Windows, or WSL.
Quick Reference Cheat Sheet
| OS | Find PID | Kill Gracefully | Kill Forcefully |
|---|---|---|---|
| macOS / Linux | sudo lsof -i :PORT | kill PID | kill -9 PID |
| Linux only | fuser PORT/tcp | kill PID | fuser -k PORT/tcp |
| Any (by name) | pgrep process-name | pkill process-name | pkill -9 process-name |
| Windows (CMD) | netstat -ano | findstr :PORT | — | taskkill /PID PID /F |
| Windows (PS) | Get-Process -Id (Get-NetTCPConnection -LocalPort PORT).OwningProcess | — | Stop-Process -Id PID -Force |
Understanding SIGTERM vs SIGKILL
Before reaching for -9, it helps to understand what signals you’re sending.
kill PIDsendsSIGTERM(signal 15) — a polite request to terminate. The process can catch this signal, clean up open files, flush buffers, and exit gracefully. Always try this first.kill -9 PIDsendsSIGKILL(signal 9) — an immediate, unblockable termination issued by the kernel. The process has no chance to clean up. Use this only when SIGTERM doesn’t work.
Data loss risk: Forceful termination can corrupt in-progress writes or leave lock files behind. Always try graceful termination first.
macOS
Step 1: Find the Process ID (PID)
sudo lsof -i :3000
Replace 3000 with your actual port number. The output looks like:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
node 8432 aditya 23u IPv6 0xabc 0t0 TCP *:3000 (LISTEN)
Copy the PID from the second column — 8432 in this example.
Step 2: Kill the Process
kill 8432
If the process doesn’t terminate within a few seconds, force it:
kill -9 8432
# or if it's owned by another user:
sudo kill -9 8432
One-Liner (macOS)
Skip the two-step lookup and kill in a single command:
sudo kill -9 $(lsof -t -i:3000)
lsof -t outputs only the PID, which gets passed directly to kill.
Linux
Option 1: lsof (same as macOS)
sudo lsof -i :3000
Then kill by PID:
kill PID
# or forcefully:
kill -9 PID
Option 2: fuser (Linux-specific, faster)
fuser is built for exactly this — finding what’s using a file or port:
fuser 3000/tcp
This prints the PID directly. To kill it in one step:
fuser -k 3000/tcp
Add -9 to force kill:
fuser -k -9 3000/tcp
Option 3: Kill by Process Name with pkill
If you know the process name (e.g., node, python, ruby), skip the PID lookup entirely:
pkill node
# or forcefully:
pkill -9 node
To see which processes pkill would target without actually killing them:
pgrep -l node
This lists the PID and name of every matching process so you can confirm before killing.
Option 4: Live Process View with top / htop
For a general overview of all running processes:
top
# or the more readable version (install separately):
htop
Press k inside top to kill a process by PID without leaving the interface.
Windows
Command Prompt
Step 1: Find the PID
netstat -ano | findstr :3000
Output:
TCP 0.0.0.0:3000 0.0.0.0:0 LISTENING 8432
The last column is the PID (8432).
Step 2: Kill the Process
taskkill /PID 8432 /F
The /F flag forces termination (equivalent to SIGKILL). Verify the port is free:
netstat -ano | findstr :3000
If nothing is returned, the port is free.
PowerShell (One-Liner)
PowerShell lets you combine both steps:
Stop-Process -Id (Get-NetTCPConnection -LocalPort 3000).OwningProcess -Force
Or to see the process name before killing:
Get-Process -Id (Get-NetTCPConnection -LocalPort 3000).OwningProcess
WSL (Windows Subsystem for Linux)
If you’re running a server inside WSL and the port appears busy from Windows (or vice versa), the process may be running in a different network namespace.
Check inside WSL:
sudo lsof -i :3000
# or
fuser 3000/tcp
Check from Windows side (if WSL process is exposed):
netstat -ano | findstr :3000
If the port is occupied by a Windows process that’s interfering with your WSL server, kill it from the Windows side using taskkill.
Troubleshooting
”Port still in use” after killing
The process may have spawned child processes. Kill the entire process tree:
# Linux / macOS — kill process and all children
kill -9 -PID
# Note the negative sign before PID — this sends SIGKILL to the entire process group
On Windows:
taskkill /PID 8432 /F /T
The /T flag terminates child processes too.
”Permission denied” when killing
The process is owned by root or another user. Use sudo:
sudo kill -9 PID
“No such process” error
The process already terminated on its own, but the OS hasn’t released the port yet (TIME_WAIT state). Wait 30–60 seconds and try again. If the issue persists, you can reuse the port immediately with socket option SO_REUSEADDR in your server code.
Can’t find PID with lsof on macOS
Make sure you’re using sudo. Without it, lsof may not have permission to inspect all sockets:
sudo lsof -i :3000
Best Practices
| Situation | Recommended Command |
|---|---|
| First attempt, unknown process | kill PID (SIGTERM) |
| Process ignores SIGTERM | kill -9 PID (SIGKILL) |
| Know the process name | pkill process-name |
| Port busy on Linux, want it fast | fuser -k PORT/tcp |
| Windows CMD | taskkill /PID PID /F |
| Windows PowerShell | Stop-Process -Id PID -Force |
| Kill process + children | kill -9 -PGID (Linux) / taskkill /PID PID /F /T (Windows) |
These commands belong in every developer’s muscle memory. Bookmark this page for the next time a zombie process is holding your port hostage.
Written by
Aditya RawasFull-stack engineer writing deep-dives on JavaScript, TypeScript, React, AWS, Docker, and Kubernetes. Passionate about making complex engineering concepts accessible to developers at every level.