Node.js Error

Error: listen EADDRINUSE: address already in use

error code: EADDRINUSE

Complete reference — what it means, how to kill the conflicting process on every OS, and how to prevent it permanently.

Quick Answer: Node.js throws Error: listen EADDRINUSE: address already in use :::3000 when another process already owns that port. On macOS/Linux, run lsof -ti:3000 | xargs kill to free it instantly. On Windows, use netstat -ano | findstr :3000 then taskkill /PID <pid> /F. On any platform: npx kill-port 3000. If it keeps coming back, you are pressing Ctrl+Z (suspend) instead of Ctrl+C (terminate).

What is EADDRINUSE?

EADDRINUSE stands for Error ADDRess IN USE — a POSIX system error thrown when a server attempts to listen() on a TCP/UDP port that is already bound by another process. The operating system enforces that only one process can own a given port at a time, so the bind call fails immediately.

Node.js surfaces this as an Error object with code: 'EADDRINUSE' and a port property that tells you exactly which port is conflicted.

Exact error messages you will see:
Error: listen EADDRINUSE: address already in use :::3000
Error: listen EADDRINUSE: address already in use 0.0.0.0:8080
Error: listen EADDRINUSE: address already in use :::5000
Error: listen EADDRINUSE: address already in use :::8000

Full Error Example

Error: listen EADDRINUSE: address already in use :::3000
    at Server.setupListenHandle [as _listen2] (node:net:1872:16)
    at listenInCluster (node:net:1920:12)
    at Server.listen (node:net:2008:7)
    at Function.listen (/project/node_modules/express/lib/application.js:635:24)
    at Object.<anonymous> (/project/app.js:10:5) {
  code: 'EADDRINUSE',
  errno: -98,
  syscall: 'listen',
  address: '::',
  port: 3000
}

The port field in the error object tells you exactly which port is conflicted — use that number in the kill commands below. The address: '::' means the server was trying to bind to all IPv6 interfaces; 0.0.0.0 means all IPv4 interfaces.

Fix 1 – Kill the process using the port

The fastest fix: find the PID of the process holding the port and terminate it.

macOS / Linux

# See which process is listening on port 3000
lsof -i :3000

# Kill it in one command (replace 3000 with your port)
lsof -ti:3000 | xargs kill

# Force kill if it ignores SIGTERM
lsof -ti:3000 | xargs kill -9

Windows (Command Prompt)

:: Find the PID using port 3000
netstat -ano | findstr :3000

:: Kill by PID (replace 12345 with the actual PID)
taskkill /PID 12345 /F

Windows (PowerShell — one-liner)

# Find and kill the process on port 3000 in one command
Get-Process -Id (Get-NetTCPConnection -LocalPort 3000).OwningProcess | Stop-Process -Force

Fix 2 – npx kill-port (cross-platform, easiest)

The kill-port package works on macOS, Linux, and Windows without knowing the right OS-specific commands. No global install needed — just use npx.

# Kill a single port
npx kill-port 3000

# Kill multiple ports at once
npx kill-port 3000 8080 5000

# Add to package.json for convenience
# "scripts": { "kill": "kill-port 3000" }
Tip: Add "predev": "npx kill-port 3000" to your package.json scripts so the port is always freed before npm run dev starts.

Fix 3 – Stop nodemon correctly: Ctrl+C not Ctrl+Z

The most common reason EADDRINUSE keeps coming back after you think you stopped the server is pressing Ctrl+Z instead of Ctrl+C.

Key comboSignal sentEffect on the port
Ctrl+C SIGINT Terminates the process cleanly. Port is released. ✓
Ctrl+Z SIGTSTP Suspends the process into the background. Port is still held. ✗

A Ctrl+Z-suspended process continues to own its port. When you start the server again, you get EADDRINUSE because the suspended instance is still there. Fix by killing the suspended process, then always use Ctrl+C going forward.

# List all suspended/background jobs in the current shell
jobs -l

# Kill the suspended Node.js process on your port
lsof -ti:3000 | xargs kill

# Or bring it to foreground first, then Ctrl+C it
fg %1

Fix 4 – macOS: Disable AirPlay Receiver (port 5000 / 7000)

On macOS Monterey (12) and later, the system's AirPlay Receiver and Control Center occupy ports 5000 and 7000 by default. If your Node.js app tries to bind to either of those ports, you will get EADDRINUSE even with no other developer process running.

# Confirm AirPlay is using port 5000
lsof -i :5000
# Output will show: ControlCe ... (ControlCenter)

Fix A — Disable AirPlay Receiver:
System Settings → General → AirDrop & Handoff → AirPlay Receiver → Off

Fix B — Change your app's port: Use port 3000, 8080, or any port above 1024 that is not reserved.

Fix 5 – Docker port mapping conflict

When running Node.js in Docker, EADDRINUSE can occur at two different levels:

# Check which process on the host owns port 3000
lsof -i :3000

# In docker-compose.yml — change the HOST port (left side) if it conflicts
services:
  app:
    ports:
      - "3001:3000"   # map host 3001 → container 3000

# Or stop the conflicting container first
docker ps                          # find the container using the port
docker stop <container_id>
docker run -p 3000:3000 myapp

Fix 6 – Multiple app.listen() calls

Calling app.listen() (or server.listen()) more than once on the same port causes EADDRINUSE immediately. This happens when:

// BAD – calling listen twice
const app = express();
const server = app.listen(3000); // first call
app.listen(3000);                // Error: EADDRINUSE

// GOOD – export the app without starting it, let the entry point start it once
// app.js
const app = express();
module.exports = app;

// server.js (entry point only)
const app = require('./app');
app.listen(3000, () => console.log('Server started'));
// In tests – use a random port or don't auto-start
const app = require('./app');

// Use port 0 to let the OS assign a free port automatically
const server = app.listen(0);
const { port } = server.address();

afterAll(() => server.close());

Fix 7 – Change the port

If you don't control the conflicting process (a system service, another app), use a different port. Always read the port from an environment variable so it's easy to override.

const PORT = process.env.PORT || 3001;

app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

Fix 8 – Handle the error and retry automatically

For CLI tools or dev servers that need to find a free port without user intervention:

function startServer(port) {
  const server = app.listen(port)
    .on('error', (err) => {
      if (err.code === 'EADDRINUSE') {
        console.log(`Port ${port} in use, trying ${port + 1}...`);
        startServer(port + 1);
      } else {
        throw err;
      }
    })
    .on('listening', () => {
      console.log(`Server started on port ${port}`);
    });
  return server;
}

startServer(3000);

Prevention – Graceful shutdown

The most common cause of persistent EADDRINUSE in development is a Node.js process that crashed or was force-quit without releasing the port. A shutdown handler closes the server cleanly before the process exits, so the port is always freed.

const server = app.listen(3000);

function shutdown(signal) {
  console.log(`Received ${signal}, shutting down...`);
  server.close(() => {
    console.log('Server closed, port released');
    process.exit(0);
  });

  // Force exit if close() hangs (e.g. keep-alive connections)
  setTimeout(() => {
    console.error('Forced exit after timeout');
    process.exit(1);
  }, 5000).unref();
}

process.on('SIGTERM', () => shutdown('SIGTERM')); // Docker stop, Kubernetes
process.on('SIGINT',  () => shutdown('SIGINT'));  // Ctrl+C in terminal
nodemon tip: Add "signal": "SIGTERM" to your nodemon.json so nodemon sends SIGTERM (triggering your shutdown handler) rather than force-killing the process on every restart.

Common causes

SituationSolution
Previous dev server crashed without releasing portKill with lsof -ti:PORT | xargs kill
Two terminals running the same projectClose one, or use different ports per instance
Pressed Ctrl+Z instead of Ctrl+C — process suspended in backgroundRun lsof -ti:PORT | xargs kill; always use Ctrl+C to stop
Another app (nginx, Apache, Postgres) using the portChange your app's port or stop the other service
macOS AirPlay Receiver occupying port 5000 or 7000Disable AirPlay Receiver in System Settings, or use a different port
nodemon restarting before old process fully exitsAdd graceful shutdown handler; add --delay 1000 to nodemon as a temporary fix
Multiple app.listen() calls in codebaseSearch for all .listen( calls; start the server only once from the entry point
Docker container host port already boundChange the host port in docker run -p HOST:CONTAINER or stop the conflicting container

Frequently Asked Questions

What is EADDRINUSE in Node.js?

EADDRINUSE stands for Error ADDRess IN USE. Node.js throws it when a server tries to listen() on a TCP port already occupied by another process. The OS enforces that only one process can own a port at a time, so the bind call fails immediately with this error.

How do I fix EADDRINUSE on macOS or Linux?

Run lsof -ti:3000 | xargs kill (replace 3000 with your port). This finds the PID of the process holding the port and kills it in one command. If the process resists, add kill -9: lsof -ti:3000 | xargs kill -9.

How do I fix EADDRINUSE on Windows?

Run netstat -ano | findstr :3000 to find the PID in the last column, then taskkill /PID <pid> /F to kill it. In PowerShell one-liner: Get-Process -Id (Get-NetTCPConnection -LocalPort 3000).OwningProcess | Stop-Process -Force.

Why does EADDRINUSE keep coming back with nodemon?

You are pressing Ctrl+Z instead of Ctrl+C. Ctrl+Z sends SIGTSTP which suspends the process into the background — it stays alive and holds the port. Ctrl+C sends SIGINT which terminates the process and releases the port. Kill the suspended process with lsof -ti:PORT | xargs kill, then always use Ctrl+C to stop nodemon.

Why does EADDRINUSE happen on port 5000 or 7000 on macOS?

On macOS Monterey and later, AirPlay Receiver uses port 5000 and Control Center uses port 7000. Disable AirPlay Receiver in System Settings → General → AirDrop & Handoff, or change your Node.js app to a different port (3000, 8080, etc.).

What causes EADDRINUSE when app.listen() is only called once?

Check for indirect double-starts: a module that calls listen() when imported, a test file that both imports the app (which auto-starts) and starts the server itself, or a hot-reload tool restarting before the old instance shuts down. Search your codebase for all .listen( calls. Use listen(0) in tests to let the OS assign a free port automatically.

Debugging Checklist

  1. Read err.port from the error object to confirm which port is conflicted.
  2. Run lsof -i :PORT (macOS/Linux) or netstat -ano | findstr :PORT (Windows) to identify the process name and PID.
  3. Check if you pressed Ctrl+Z recently — run jobs -l to see suspended processes.
  4. Check if you have multiple terminal tabs or windows running the same project.
  5. On macOS, if the port is 5000 or 7000, check if AirPlay Receiver is the owner.
  6. Search your codebase for all .listen( calls to confirm the server is only started once.
  7. Check if a system service (web server, database, message broker) uses that port by default.
  8. Add a graceful shutdown handler (SIGTERM + SIGINT) to prevent leaving orphaned processes.
Common mistake: Using kill -9 (or taskkill /F on Windows) as the first resort skips the graceful shutdown signal. Always try kill (SIGTERM) first — it allows the process to close connections and release resources cleanly. Use -9 / /F only if the process does not respond to SIGTERM within a few seconds.

Related Errors