Skip to content
DevOps2026-09-073 min read

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

  1. Fix a team port convention: 3000/3001/3002 assigned by project — no fighting;
  2. Use a PORT env var: put PORT=3001 in .env, reference the variable instead of hardcoding;
  3. 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;
  4. 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

  1. lsof -ti:3000 (Windows: netstat -ano | findstr :3000) to get the PID
  2. Check the process name: your leftover → kill; someone else's service → move ports; docker-proxy → stop the container
  3. Still busy after killing: wait out TIME_WAIT or hunt the supervisor
  4. 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.


Advertisement