Port 3000 already in use? Find and stop the ghost dev servers

EADDRINUSE? A dev server from a closed terminal or coding agent is still listening. Find what's on a port on macOS, Linux and Windows and stop it safely.

Nov 1, 20263 min read

300043215173800087879229
A dev server holding a portA cable: a browser tab or client connected right nowNo cable, hours old: an idle server, safe to stopFive dev servers on a typical evening. Three have something connected; two were left behind by a closed terminal or an agent and hold their ports and memory until someone notices. Gigaflush only suggests the ones with no connection.

You run npm run dev and get this instead of a server:

Error: listen EADDRINUSE: address already in use :::3000

Nothing is wrong with your project. Something else is already listening on that port, and nine times out of ten it’s you: a dev server from yesterday, still running in a terminal tab you closed, or one an AI coding agent started to check its work and never stopped.

Where ghost servers come from

  • Closed terminals. Closing a tab or window doesn’t always stop what’s in it. Tools started with &, nohup, or a process manager keep going, and their parent becomes launchd (macOS) or init (Linux). That’s an orphaned process.
  • Coding agents. Claude Code, Cursor, Codex and friends happily start vite, next dev or python -m http.server to test a change. When the session ends, the server often doesn’t.
  • Port hopping. Vite and Astro quietly move to the next free port. So the ghost on :5173 doesn’t error; you just end up on :5174, :5175… and the old ones never die.

Each one holds a port and some memory, and many keep file watchers running. Here’s what a typical evening looks like. Press a hollow socket, or let it play:

558 MBheld by idle dev servers
3idle3in use
localhost6 listening
:3000:4321:4322:5173:8000:8787
Idle · safe to stop
marketing-site:4322 · astro previeworphaned 7d182 MB
dashboard:3000 · next devorphaned 1d312 MB
api:8000 · manage.py runserverup 9h64 MB
In use · left alone
studio-web:5173 · viteConnected
docs:4321 · astro devConnected
edge:8787 · wrangler devStarted 25m ago
Stopping works like Ctrl+C.

Step 1: find what’s listening

lsof -nP -iTCP:3000 -sTCP:LISTEN

Drop :3000 to list every listening port: lsof -nP -iTCP -sTCP:LISTEN

That gives you a PID. Before you stop it, find out what it actually is and where it runs from:

ps -o pid,ppid,etime,%cpu,command -p <PID>
lsof -a -d cwd -p <PID>

PPID 1 means orphaned. etime is how long it has been running; cwd is the project folder.

Step 2: check nobody is using it

A server you forgot isn’t automatically a server nobody needs. If a browser tab, a mobile simulator or another service is connected right now, there’s an established connection on that port:

lsof -nP -iTCP:3000 -sTCP:ESTABLISHED

Empty output, a process that has been up for hours, and a parent PID of 1: that’s a ghost.

Step 3: stop it politely

kill <PID>        # SIGTERM, like Ctrl+C
kill -9 <PID>     # only if it ignores the first one

What about killall node or pkill -f node? Please don’t. Your editor’s language servers, MCP servers, the agent you’re using and half your desktop apps may be Node processes too.

Or let Gigaflush find them

Gigaflush’s Servers tab does the checks above for every port at once, every time you open it, and only suggests a server when all of these are true:

  1. It’s your process, and a dev runtime: Node, Bun, Deno, Python, Ruby or PHP. Databases, Docker, editors, agents and language servers are never listed.
  2. Nothing is connected to its ports.
  3. It has run for at least 30 minutes at under 2% CPU on average.
  4. It’s orphaned, or it has been running for 6 hours or more.

The patch panel shows every dev server you have running, so you can see what was left alone and why. Stopping one checks it again by PID and start time, then sends SIGTERM. Child processes such as bundlers and workerd get the same request. The whole check takes about 0.2 seconds and nothing runs in the background. It’s available on macOS and Linux; Windows is next.

Keep it from happening again

  • Pin the port so a ghost fails loudly instead of pushing you to the next port: vite --strictPort, or server.strictPort: true in your Vite config.
  • Tell your agent to clean up. Add a line to your CLAUDE.md or AGENTS.md file, or your Cursor rules: “Stop any dev server you start before finishing.”
  • Prefer foreground servers in a terminal you can see, over & and nohup.

Keep reading