Port Already in Use? The Complete EADDRINUSE Fix Guide (macOS/Linux/Windows)
Symptom: your project won't start, one error line
Error: listen EADDRINUSE: address already in use :::3000
or the friendlier variant:
Port 3000 is already in use
In one sentence: the port you want to listen on is already taken by another process. A dev server, a database, a Docker container, or a leftover node process from a previous run — any of them could be the squatter.
Step one: find who owns the port
macOS / Linux
lsof -ti:3000 # prints just the PID(s) — script friendly
lsof -i:3000 # human form: PID, process name, user
Windows (PowerShell)
netstat -ano | findstr :3000 # last column is the PID
tasklist /fi "PID eq 12345" # resolve the PID to a process name
Step two: deal with the occupant
Case A: your own old dev server (most common)
Last time's Ctrl+C didn't fully clean up, or you closed the terminal while the process lived on. End it:
# macOS / Linux
kill -9 $(lsof -ti:3000)
# Windows (PowerShell)
taskkill /PID 12345 /F
How to tell: the process name from lsof -i:3000 is yours (node, vite, next-server...) → safe to kill.
Case B: another service owns the port
The process isn't your project (postgres, mongod, another team's service) — don't kill it; change your port:
# per-framework port flags
next dev -p 3001 # Next.js
vite --port 3001 # Vite
npm start -- --port 3001 # most CLIs accept it
Case C: a Docker container maps the port
The process name shows docker-proxy:
docker ps --filter "publish=3000" # find the container
docker stop <name> # stop it, or change the compose port mapping
Why kill -9? Isn't plain kill enough?
Try plain kill first (SIGTERM — lets the process exit gracefully); escalate to kill -9 (SIGKILL) only if it survives a few seconds. Leftover dev processes rarely need grace, so -9 is fine; stateful production services (databases) should always get SIGTERM first.
Prevention: never fight over ports again
- Fix a team port convention: 3000/3001/3002 assigned by project — no fighting;
- Use a PORT env var: put
PORT=3001in.env, reference the variable instead of hardcoding; - Exit with Ctrl+C, not by closing the terminal: most dev servers clean up with the session — but background launches (
&/nohup) do not; record PIDs of anything you background; - Port 0 = let the OS pick: tests and scripts listening on port 0 get a free port automatically — conflict eliminated by construction.
FAQ
lsof says port 3000 belongs to ControlCenter on my Mac?
Since macOS Monterey, system services reserve assorted ports (AirPlay Receiver commonly takes 5000/7000). ControlCenter holding 3000 is system behavior — don't kill it; just run your app on another port.
Killed the process but the port is still busy?
Two possibilities: ① the socket sits in TIME_WAIT (auto-releases within a minute — retry shortly); ② a parent supervisor respawned it (Docker restart policy, pm2, systemd) — stop the supervisor instead of repeatedly killing children.
What are the three colons in :::3000?
::: is the IPv6 any-address :: plus the port separator — your service tried to listen on IPv6 port 3000. It's a different socket from IPv4's 0.0.0.0:3000, but most programs dual-stack share them, so treat it as plain occupancy.
One-liner alias
# macOS/Linux: free a port
freeport() { lsof -ti:"$1" | xargs kill -9 2>/dev/null && echo "port $1 freed" || echo "port $1 was free"; }
# usage: freeport 3000
Windows (PowerShell profile):
function freeport($p) { Get-NetTCPConnection -LocalPort $p -ErrorAction SilentlyContinue |
ForEach-Object { Stop-Process -Id $_.OwningProcess -Force } }
The checklist
lsof -ti:3000(Windows:netstat -ano | findstr :3000) to get the PID- Check the process name: your leftover → kill; someone else's service → move ports; docker-proxy → stop the container
- Still busy after killing: wait out TIME_WAIT or hunt the supervisor
- Long term: ports in .env, PIDs for backgrounded processes, port 0 in scripts
Provided by ToolVault. Related tools: Dockerfile Generator, Linux Cheatsheet, HTTP Status Codes. See the homepage for more developer tools.
Related Tools
Related Articles
Subnetting / CIDR Explained: What Is 192.168.1.0/24 and How to Calculate
What does CIDR like 192.168.1.0/24 mean, how to get the subnet mask, how to compute usable hosts? Explains subnetting with intuitive examples, plus our CIDR / subnet calculator.
Online Dockerfile Generator: Quickly Create Docker Container Configs
Learn how to use an online tool to quickly generate Dockerfiles. Understand Dockerfile basic instructions, multi-stage build optimization, and containerization best practices.