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.
Error: listen EADDRINUSE: address already in use :::3000Error: listen EADDRINUSE: address already in use 0.0.0.0:8080Error: listen EADDRINUSE: address already in use :::5000Error: 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" }
"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 combo | Signal sent | Effect 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:
- Inside the container — two processes inside the same container trying to use the same port. Fix exactly as above.
- On the host — the host port in
-p HOST:CONTAINERis already in use by another process or container.
# 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:
- A module is imported in two places and both trigger
listen() - A test file imports the app module which auto-starts the server, and the test framework also starts it
- Hot-reload configuration starts the server twice during development
// 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
"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
| Situation | Solution |
|---|---|
| Previous dev server crashed without releasing port | Kill with lsof -ti:PORT | xargs kill |
| Two terminals running the same project | Close one, or use different ports per instance |
| Pressed Ctrl+Z instead of Ctrl+C — process suspended in background | Run lsof -ti:PORT | xargs kill; always use Ctrl+C to stop |
| Another app (nginx, Apache, Postgres) using the port | Change your app's port or stop the other service |
| macOS AirPlay Receiver occupying port 5000 or 7000 | Disable AirPlay Receiver in System Settings, or use a different port |
| nodemon restarting before old process fully exits | Add graceful shutdown handler; add --delay 1000 to nodemon as a temporary fix |
Multiple app.listen() calls in codebase | Search for all .listen( calls; start the server only once from the entry point |
| Docker container host port already bound | Change 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
- Read
err.portfrom the error object to confirm which port is conflicted. - Run
lsof -i :PORT(macOS/Linux) ornetstat -ano | findstr :PORT(Windows) to identify the process name and PID. - Check if you pressed Ctrl+Z recently — run
jobs -lto see suspended processes. - Check if you have multiple terminal tabs or windows running the same project.
- On macOS, if the port is 5000 or 7000, check if AirPlay Receiver is the owner.
- Search your codebase for all
.listen(calls to confirm the server is only started once. - Check if a system service (web server, database, message broker) uses that port by default.
- Add a graceful shutdown handler (
SIGTERM+SIGINT) to prevent leaving orphaned processes.
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
ECONNREFUSED: connection refused— the opposite problem: nothing is listening on the port you are trying to connect toECONNRESET: connection reset by peer— the connection was established but then forcibly closed mid-sessionENOTFOUND: getaddrinfo ENOTFOUND— DNS resolution failure when connecting to a hostnameEACCES: permission denied— trying to bind to a privileged port (below 1024) without root/administrator rights; fix withsudoor use a port above 1024