"Port 3000 is already in use": Find It and Free It, Every OS
⏱️ 2 min read
Error: listen EADDRINUSE :::3000 (or "address already in use") means exactly one thing: another process holds the port. Find it, decide whether it's a zombie or a service you need, then either kill it or change your app's port.
Find the holder
Linux: ss -tlnp | grep :3000 (or lsof -i :3000). macOS: lsof -nP -iTCP:3000 -sTCP:LISTEN. Windows: netstat -ano | findstr :3000 then map the PID in Task Manager. Inside Docker, the holder is usually another container mapping the same host port — docker ps shows the mapping column.
Kill it (if it's yours)
kill <PID>, and only escalate to -9 if it ignores you. On Windows: taskkill /PID <pid> /F. The two zombie classics: a previous run of your own dev server that nodemon never cleaned up, and a Docker container that exited but whose port mapping was taken over by docker-proxy — the fix there is docker rm, not kill.
Why "still in use" after you killed it
1. TIME_WAIT sockets. A killed server leaves connections in TIME_WAIT for up to 2 minutes; a server with SO_REUSEADDR (most frameworks set it) rebinds fine, but a strict one refuses. Wait 60s or set the socket option in your app.
2. Something re-spawned it. systemd, pm2, docker restart policy, or nodemon watching your kill and restarting. systemctl status or pm2 list before assuming ghosts.
The structural fix
Dev servers should read the port from env (PORT=3001 npm start) so collisions never block you, and container ports should differ per service in compose. Setting up a reproducible dev environment on a server you control — an hourly VM — removes the laptop-zombie problem entirely.