Skip to main content
Node.js7 min read2026-03-01

Node.js Port Already in Use (Error: listen EADDRINUSE)

Comprehensive step-by-step fix for Error: listen EADDRINUSE: address already in use :::3000 in Node.js, Express, Next.js, and Docker across macOS, Linux, and Windows.

Error Code / Stack Trace

Error: listen EADDRINUSE: address already in use :::3000 at Server.setupListenHandle [as _listen2] (net.js:1317:16) at listenInCluster (net.js:1365:12) at Server.listen (net.js:1451:7)

Problem Overview

A Node.js or Express HTTP server fails to bind to the requested TCP port (typically 3000, 5000, or 8080) because the operating system networking stack reports that the socket address is already bound and actively listened to by another process, or remains locked in a lingering TIME_WAIT state.

Why Does This Happen?

  • A previous Node.js process crashed or was suspended in the background (common with Ctrl+Z instead of Ctrl+C) without closing its TCP socket listener.
  • Another development server (such as Next.js, Vite, React dev server, or a Docker container) is already actively listening on port 3000.
  • Development file watchers like nodemon or tsx spawned orphan child worker processes that failed to terminate upon file restart.
  • On macOS (Monterey, Ventura, Sonoma, Sequoia), the system AirPlay Receiver or Control Center service occupies ports 5000 and 7000 by default.
  • A Docker container has published the host port (-p 3000:3000) and is running in the background.
  • In Windows / WSL2, a socket collision occurred between the Windows host networking layer and the WSL virtualized interface.

Step-by-Step Solution

Step 1: Identify the Process ID (PID) Using the Port

Use OS-native diagnostic utilities to identify the exact PID and process name holding the socket lock on port 3000.

bash
# macOS and Linux:
lsof -i :3000
# Alternative Linux socket statistics:
ss -lptn 'sport = :3000'

# Windows (PowerShell):
Get-Process -Id (Get-NetTCPConnection -LocalPort 3000).OwningProcess

# Windows (Command Prompt):
netstat -ano | findstr :3000

Step 2: Safely Terminate the Conflicting Process

Send a termination signal to release the TCP socket. Use graceful SIGTERM first, followed by SIGKILL if the process is unresponsive.

bash
# macOS and Linux (graceful then force):
kill -15 <PID>
# Or force kill if hung:
kill -9 <PID>

# Fast one-liner to kill whatever is on port 3000 (Linux/macOS):
fuser -k 3000/tcp
# Or with npx utility:
npx kill-port 3000

# Windows (Command Prompt):
taskkill /F /PID <PID>

# Windows (PowerShell):
Stop-Process -Id <PID> -Force

Step 3: Check and Stop Conflicting Docker Containers

If lsof or netstat points to 'com.docker.backend' or 'docker-proxy', inspect and stop active container port bindings.

bash
# Find container bound to port 3000:
docker ps --filter 'publish=3000'

# Stop the container:
docker stop <container_id_or_name>

Step 4: Disable macOS AirPlay Receiver (If Port 5000 or 7000 Fails)

macOS uses port 5000 for AirPlay Receiver by default. If your Express or Flask app uses port 5000, disable AirPlay Receiver in System Settings > General > AirDrop & AirPlay > Toggle off 'AirPlay Receiver'.

bash
# Or quickly run your dev server on a non-conflicting port:
PORT=5001 npm run dev

Step 5: Implement Dynamic Port Configuration and Fallback in Code

Never hardcode port numbers. Configure your Node.js application to check environment variables, command-line arguments, or fallback to an available port.

typescript
import express from 'express';

const app = express();

// Read port from environment or fallback to 3000
const DEFAULT_PORT = parseInt(process.env.PORT || '3000', 10);

function startServer(port: number) {
  const server = app.listen(port, () => {
    console.log(`Server actively running on http://localhost:${port}`);
  });

  server.on('error', (err: NodeJS.ErrnoException) => {
    if (err.code === 'EADDRINUSE') {
      console.warn(`Port ${port} is in use, attempting fallback to port ${port + 1}...`);
      startServer(port + 1);
    } else {
      console.error('Server error:', err);
    }
  });
}

startServer(DEFAULT_PORT);

Step 6: Implement Graceful Shutdown to Prevent Zombie Sockets

Ensure Node.js intercepts operating system termination signals (SIGINT from Ctrl+C and SIGTERM from hosting platforms/Docker) and closes open server sockets cleanly.

typescript
import http from 'http';
import app from './app';

const server = http.createServer(app);
const PORT = process.env.PORT || 3000;

server.listen(PORT, () => {
  console.log(`Application listening on port ${PORT}`);
});

// Clean socket shutdown handlers
function shutdown(signal: string) {
  console.log(`Received ${signal}. Closing HTTP server gracefully...`);
  server.close(() => {
    console.log('HTTP server closed. Exiting process.');
    process.exit(0);
  });

  // Force close after 10s if connections refuse to finish
  setTimeout(() => {
    console.error('Forcefully terminating process due to lingering connections.');
    process.exit(1);
  }, 10000);
}

process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));

Alternative Workarounds

Using kill-port CLI utility for instant one-command fixes

Install or run the kill-port npx utility directly without manually parsing PIDs in terminal.

text
npx kill-port 3000 8080 5000

Assigning Ephemeral Port (Port 0) for automated test suites

In Vitest or Jest integration tests, listening on port 0 directs the OS kernel to assign an available ephemeral port dynamically, preventing port collisions across parallel test workers.

text
const server = app.listen(0, () => {
  const address = server.address();
  const assignedPort = typeof address === 'object' && address ? address.port : 0;
  console.log(`Test runner running on random open port ${assignedPort}`);
});

Common Mistakes to Avoid

  • •Suspending the dev server process with Ctrl+Z instead of properly stopping it with Ctrl+C, leaving the socket held by a stopped background job in terminal.
  • •Hardcoding port 3000 across multiple microservices or frontend and backend apps on the same development workstation.
  • •Running multiple instances of nodemon or tsx with legacy watch mode flags that fail to kill previous child process trees on crash.
  • •Forgetting that Docker containers bind ports on the host interface, preventing local Node processes from binding the identical port.

Prevention & Best Practices

  • Use a .env file and process.env.PORT for every service so team members can configure individual ports easily.
  • Configure npm scripts to include pre-dev cleanup if orphan processes are common: 'predev': 'npx kill-port 3000 || true'.
  • Always register process.on('SIGINT') and process.on('SIGTERM') handlers in production microservices and local tools.
  • In Docker Compose setups, assign distinct host port ranges for different projects (e.g., 3001:3000, 3002:3000).

Frequently Asked Questions

What does 'EADDRINUSE: address already in use :::3000' mean with the triple colons?

The triple colons (:::3000) represent the IPv6 unspecified address (equivalent to 0.0.0.0 in IPv4). Modern Node.js binds to dual-stack IPv6 by default. It means another process is already bound to port 3000 on either IPv6 or IPv4.

Why does lsof -i :3000 return nothing on Linux even when the error occurs?

On Linux, if the process belongs to another user or was started by root/Docker/systemd, running lsof without root permissions will fail to show the process. Always prepend sudo: sudo lsof -i :3000 or sudo ss -tulpn | grep 3000.

Why does nodemon constantly cause EADDRINUSE on save?

nodemon sends SIGUSR2 by default to restart your app. If your code has asynchronous cleanup or keeps open database pools or TCP keep-alive sockets, the new process attempts to bind the port before the old process finishes unbinding. Handle process.once('SIGUSR2') to close the server gracefully.

How do I fix port 5000 in use on macOS Monterey / Ventura / Sonoma?

Apple uses port 5000 for the AirPlay Receiver server. Navigate to macOS System Settings > General > AirDrop & AirPlay, and toggle off 'AirPlay Receiver', or configure your Node app to use port 5001 or 3000 instead.

Can two processes listen on the same port using SO_REUSEPORT in Node.js?

In standard Node.js net/http modules, no. Only the Node.js cluster module uses the operating system's internal socket sharing to distribute incoming requests across master and worker child processes on the same port.