When a server reports EADDRINUSE, first identify the process that owns the port instead of killing unrelated tasks. On macOS or Linux use lsof -t -i:3000, then stop the returned PID; in PowerShell use Get-NetTCPConnection -LocalPort 3000 and pass its OwningProcess to Stop-Process. Use force only after confirming the process and port.
How to Find and Kill a Process by Port Number (Mac, Windows & Linux)
Stuck on "Error: port already in use"? Learn to find the Process ID (PID) and kill any open port on Mac, Windows, or Linux using lsof, netstat, and taskkill.
// The short answer
In this guide · 6 sections
We’ve all been there: you sit down, run your dev server, and get hit right between the eyes with an EADDRINUSE error. It’s almost always a stray background process refusing to let go of the port you need, bringing whatever you were building to a dead stop. Luckily, tracking down that runaway process and reclaiming the port only takes a couple of seconds once you know the exact commands to run.
Bash
# Mac & Linux (one-liner)
kill -9 $(lsof -t -i:3000)
# Linux (fastest)
sudo fuser -k 3000/tcp
# Windows (PowerShell)
Stop-Process -Id (Get-NetTCPConnection -LocalPort 3000).OwningProcess -ForceWhy Does This Keep Happening? (EADDRINUSE and TIME_WAIT)
Node.js and the Classic EADDRINUSE
Node apps are notorious for crashing halfway through an operation without ever closing their sockets properly. When you try firing the server up again a second later, your operating system still treats that previous run as if it’s actively listening on the port. The process ends up floating detached in the background, meaning you’ll have to look up its Process ID (PID) yourself and forcefully kick it off.
If you’re working with Node day in and day out, staying on top of how you update Node.js can also save you from a few annoying runtime bugs that leave dead ports behind.
The Mystery of the TIME_WAIT State
Every once in a while, your terminal swears a port is tied up, but searching for an active process comes up completely empty. That usually means your OS put the connection into TIME_WAIT mode on purpose to let lingering network packets clear out safely without colliding. It typically sticks around for about 60 to 120 seconds. If that's what's going on, honestly just step away for two minutes and it will sort itself out without running any kill commands.
Clearing Stuck Ports on Mac and Linux
Finding the PID with lsof and Finishing It with kill
The classic, reliable way to tackle this is a simple two-step dance. First, ask lsof who’s currently squatting on your port:
Bash
lsof -i :3000Scan the second column of the output to grab the process ID:
Plaintext
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
node 12345 alice 24u IPv6 0x... 0t0 TCP *:3000 (LISTEN)Now that you have the PID in hand, send it a standard polite exit signal:
Bash
kill 12345That sends a gentle SIGTERM, letting the app wrap things up cleanly. But if the process is completely hung and ignores you, bring out the heavy hammer with SIGKILL:
Bash
kill -9 12345Keep in mind that kill -9 drops the axe instantly and gives the app zero chance to clean up file handles, so save it for when the polite way fails. If you want to skip the manual lookup entirely, you can wrap both steps into a neat one-liner:
Bash
kill -9 $(lsof -t -i:3000)Grabbing Ports Faster with ss (Linux)
If you're on a modern Linux box, skip the aging netstat and reach for ss instead. It pulls data straight out of kernel space and returns almost instantaneously, which makes a noticeable difference on busy machines with thousands of open sockets:
Bash
ss -lptn 'sport = :3000'It spits out the binary name, connection state, and the PID right in a single glance, saving you from having to cross-check a separate list. Even better, Linux gives you fuser, which turns finding and terminating the process into one tiny command:
Bash
sudo fuser -k 3000/tcpBy the way, if your terminal throws a fit while running any of these tools, double-checking your zsh: command not found: brew paths will almost certainly get things back on track.
Reclaiming Ports on Windows
The Modern Way: PowerShell
PowerShell makes this way friendlier than the clunky old CMD days, letting you pipe the port lookup straight into a kill command without manual parsing:
PowerShell
# One-liner: find and kill whatever is on port 3000
Get-NetTCPConnection -LocalPort 3000 | Select-Object -ExpandProperty OwningProcess | Stop-Process -ForceIf you prefer to see what you're actually about to shut down before pulling the trigger, just split it up into two quick commands:
PowerShell
# Step 1: Check what's listening
Get-NetTCPConnection -LocalPort 3000
# Step 2: Terminate it
Stop-Process -Id <PID>The Old-School Route: netstat and taskkill
If you're stuck on an older Windows box or working inside plain Command Prompt, the classic combo still gets the job done:
Bash
netstat -ano | findstr :3000Look for the row marked LISTENING and grab the PID listed all the way at the end. Then tell Windows to close it:
Bash
taskkill /PID 6789 /FThat /F flag forces it down immediately, just like kill -9 does over in Unix land. And if some stubborn OS service has completely wedged the networking layer, doing a clean full system restart from CMD will wipe the slate clean as a last resort.
Edge Cases and Annoying Gotchas
When Docker is Secretly Holding the Port
Half the time this happens, the conflict isn't coming from a stray Node script at all - it's a background Docker container binding to the exact same host port. Take a second to see what Docker has running before you start nuking local processes:
Bash
docker ps --format "table {{.Names}}\t{{.Ports}}"You'll see something like this if a container is mapped to port 3000:
Plaintext
NAMES PORTS
my-app 0.0.0.0:3000->3000/tcpIf that's the culprit, don't mess with system PIDs - just tell Docker to stop the container directly:
Bash
docker stop my-appIf you run lsof and notice com.docker.backend sitting on your port, that's your smoking gun. The port is bound inside the container engine, so regular host-level kill commands won't do anything useful anyway.
Permission Errors and Zombie Services
Seeing a Permission Denied error? That just means the process was launched by another user account or root, so you'll need elevated privileges to kick it off:
Bash
sudo kill -9 12345Now, if a process dies and immediately pops right back up under a fresh PID, it’s being babysat by a daemon like systemd on Linux or launchd on macOS. In that scenario, killing the PID is pointless because the watcher will just revive it. You have to shut down the underlying service instead:
Bash
# See which binary is holding the port
lsof -i :5432 -sTCP:LISTEN
# Find the exact service unit managing it
systemctl list-units --type=service | grep postgresOnce you know the name, shut down the supervisor:
Bash
# On Linux
sudo systemctl stop postgresql
# On Mac (for Homebrew services)
brew services stop postgresqlIf you can't figure out who keeps restarting it, watching the system logs in real time usually gives it away. Tossing the Linux tail command on your main log file (sudo tail -f /var/log/syslog) will point out the culprit instantly.
Killed the Process, but the Port is Still Blocked?
Walk through this checklist in order:
1. Is the process actually dead? Run lsof -i :PORT one more time. If it’s still showing up, run the kill again with sudo and -9.
2. Port blocked, but zero processes listed? You’re dealing with a lingering TIME_WAIT state. Step away for 60 to 120 seconds and it will free itself. There is nothing to kill.
3. Does the process instantly revive with a new PID? It’s wired up as a managed service. Shut it down via systemctl stop or brew services stop instead of attacking the PID.
4. Is Docker claiming it? Run docker ps to verify. Shut down the actual container instead of hunting host processes.
5. None of the above? You might have duplicate workers or orphaned instances running from another folder. Hunt down every trace of the runtime:
Bash
# Mac & Linux
ps aux | grep node
# Windows
Get-Process nodeWipe out those remaining instances, take a breath, and fire up your server fresh.
How to Double-Check That the Port is Open
Never assume the kill went through - take two seconds to verify the socket is truly clear before starting your server back up:
Bash
# Mac & Linux
lsof -i :3000
# Linux shortcut
ss -lptn 'sport = :3000'
# Windows PowerShell
Get-NetTCPConnection -LocalPort 3000If your terminal returns silence with no output, you're good to go. Always start polite with a simple kill, reach for kill -9 only when things refuse to budge, and remember to halt the supervising service if the process keeps popping back up from the dead.
Quick answers
Frequently asked questions
How do I find what process is using a port?
On Mac and Linux, run `lsof -i :PORT` (e.g., `lsof -i :3000`). The output shows the process name and PID in the second column. On Linux you can also use `ss -lptn 'sport = :3000'` for faster results. On Windows, run `netstat -ano | findstr :3000` in Command Prompt or `Get-NetTCPConnection -LocalPort 3000` in PowerShell.
How to kill port 3000 in Node.js (EADDRINUSE error)?
Run `kill -9 $(lsof -t -i:3000)` on Mac/Linux to find and kill the Node process in one step. On Windows, use `Get-NetTCPConnection -LocalPort 3000 | Select-Object -ExpandProperty OwningProcess | Stop-Process -Force` in PowerShell. This is the most common fix for the EADDRINUSE error after a crash or improper shutdown.
Why does my port keep coming back after killing it?
The process is managed by a service controller like `systemd` (Linux) or `launchd` (Mac), which automatically restarts it. Killing the PID only stops the current instance. You need to stop the service itself: `sudo systemctl stop <service-name>` on Linux or `brew services stop <service-name>` on Mac.
What is the difference between kill and kill -9?
`kill PID` sends SIGTERM, which politely asks the process to shut down cleanly (saving files, closing connections). `kill -9 PID` sends SIGKILL, which forces the OS to terminate the process immediately with no cleanup. Always try `kill` first. Use `kill -9` only if the process ignores the graceful request.
How to kill a port without sudo (permission denied)?
Permission denied means the process was started by a different user or root. You need elevated privileges: prefix the command with `sudo` (e.g., `sudo kill -9 12345` or `sudo fuser -k 3000/tcp`). If you still cannot kill it, the process may be a protected system service that requires stopping through its service manager.
How do I check if a port is free after killing the process?
Run `lsof -i :PORT` on Mac/Linux. If the command returns no output, the port is free. On Linux you can also use `ss -lptn 'sport = :PORT'`. On Windows, run `Get-NetTCPConnection -LocalPort PORT` in PowerShell. An empty result confirms the port is released and safe to use.
How to kill a port in one command without finding the PID first?
On Mac and Linux: `kill -9 $(lsof -t -i:3000)` combines the search and kill into a single command. On Linux you can also use `sudo fuser -k 3000/tcp`. On Windows with PowerShell: `Get-NetTCPConnection -LocalPort 3000 | Select-Object -ExpandProperty OwningProcess | Stop-Process -Force`.
Something wrong or out of date?
Tools change fast. Tell the editor what no longer works.