devkult_
tools29converters34
home/blog/eaddrinuse-port-already-in-use
nodedebuggingnetworking

EADDRINUSE: Address Already in Use — How to Free the Port

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

Only one process can listen on a given port at a time. This error means something already has port 3000 — usually an earlier copy of your own server that never shut down. The fix is two steps: find the process, then stop it.

Find what's using the port

macOS and Linux:

lsof -i :3000
COMMAND   PID  USER   FD   TYPE  DEVICE SIZE/OFF NODE NAME
node    48213  alex   23u  IPv6  0x8f…      0t0  TCP *:3000 (LISTEN)

The PID column is what you need. On Linux without lsof, ss shows the same:

ss -ltnp | grep :3000

Windows (Command Prompt):

netstat -ano | findstr :3000

The last column is the PID. In PowerShell:

Get-NetTCPConnection -LocalPort 3000 | Select-Object OwningProcess

Stop it

kill 48213          # ask it to exit cleanly
kill -9 48213       # force it, if it ignores the first

Or in one line, on macOS and Linux:

kill $(lsof -ti :3000)

On Windows:

taskkill /PID 48213 /F

Before you kill anything, check the command name. If it isn't yours — a database, another app, a system service — change your app's port instead.

Why it keeps happening

  • A dev server still running in another terminal, or a background job you forgot. Close it properly with Ctrl+C rather than closing the window.
  • A crashed run left a child process behind. Tools that spawn your server (nodemon, concurrently, npm scripts) can leave an orphaned process if they're killed abruptly.
  • Your code calls listen() twice — for example, the app module starts the server and your test setup starts it again.
  • A Docker container publishes the port. docker ps shows it in the PORTS column; stop the container or map it elsewhere.
  • Port 5000 on macOS. Since macOS Monterey, the AirPlay Receiver listens on ports 5000 and 7000. Use another port, or turn off AirPlay Receiver in System Settings → General → AirDrop & Handoff.

Make your server fail clearly and exit cleanly

Read the port from the environment so you can move it without editing code, and turn the crash into a readable message:

const PORT = Number(process.env.PORT) || 3000;
const server = app.listen(PORT, () => console.log(`Listening on ${PORT}`));

server.on("error", (err) => {
  if (err.code === "EADDRINUSE") {
    console.error(`Port ${PORT} is already in use. Stop the other process or set PORT.`);
    process.exit(1);
  }
  throw err;
});

// Release the port when you press Ctrl+C or the process is told to stop
for (const signal of ["SIGINT", "SIGTERM"]) {
  process.on(signal, () => server.close(() => process.exit(0)));
}

The signal handlers matter most under process managers and containers, which send SIGTERM and wait for a clean exit before forcing one.

The same error elsewhere

The message varies, but the cause is the same:

  • Python: OSError: [Errno 98] Address already in use (Linux) or [Errno 48] (macOS)
  • Go: listen tcp :8080: bind: address already in use
  • Docker: Bind for 0.0.0.0:8080 failed: port is already allocated
  • nginx: bind() to 0.0.0.0:80 failed (98: Address already in use)

lsof -i :<port> finds the culprit for all of them. And if the server is running but the browser still can't reach it, the problem is somewhere else — for a reverse proxy, start with 502 Bad Gateway.

More from the blog