How to Fix ‘Port Already in Use’ Errors in Node.js
How to Fix ‘Port Already in Use’ Errors in Node.js
You run node server.js and immediately hit this wall:
Error: listen EADDRINUSE: address already in use :::3000
Your server never starts. Something else is already holding port 3000 hostage, and Node.js can’t claim it. This is one of the most common friction points in local development, and fixing it takes less than two minutes once you know what you’re doing.
Let me walk you through exactly how I diagnose and resolve this every time it happens.
The Root Cause
Every port on your machine can only be bound by one process at a time. When Node.js tries to listen() on a port that’s already claimed, the operating system rejects the request and throws EADDRINUSE. The culprit is almost always one of these:
- A previous Node.js server you forgot to shut down
- A crashed process that didn’t release the port cleanly
- Another application (a database, a different dev server, Docker) using the same port
- A zombie process left over from a
nodemonorpm2restart gone wrong
Step 1: Find Out What’s Using the Port
Before you kill anything, identify the offender. Replace 3000 with whatever port your app uses.
On macOS and Linux:
lsof -i :3000
You’ll see output like this:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
node 8421 alice 22u IPv6 12345 0t0 TCP *:3000 (LISTEN)
The PID column (8421 in this example) is what you need.
Alternatively, use ss or netstat on Linux:
ss -tulpn | grep :3000
# or
netstat -tulpn | grep :3000
On Windows (Command Prompt or PowerShell):
netstat -ano | findstr :3000
Output looks like:
TCP 0.0.0.0:3000 0.0.0.0:0 LISTENING 8421
The last number is the PID. To confirm which application owns it:
tasklist /FI "PID eq 8421"
Step 2: Kill the Process
Once you have the PID, terminate it.
On macOS and Linux:
kill -9 8421
The -9 flag sends SIGKILL, which forces an immediate termination. Use this when a regular kill 8421 (which sends SIGTERM) doesn’t work.
On Windows:
taskkill /PID 8421 /F
The /F flag forces termination.
Step 3: One-Liner Shortcuts
If you’re doing this repeatedly during development, these shortcuts save time.
macOS/Linux — kill whatever is on port 3000 in one command:
kill -9 $(lsof -t -i:3000)
lsof -t outputs only the PID, which gets passed directly to kill.
macOS/Linux — using npx kill-port:
npx kill-port 3000
This works without installing anything globally. You can also kill multiple ports at once:
npx kill-port 3000 3001 8080
Windows PowerShell equivalent:
Stop-Process -Id (Get-NetTCPConnection -LocalPort 3000).OwningProcess -Force
Step 4: Make Your Node.js App Handle This Gracefully
Rather than manually hunting down PIDs every time, add error handling directly to your server so you get a human-readable message and an automatic fallback option.
const express = require('express');
const app = express();
const DEFAULT_PORT = 3000;
const server = app.listen(DEFAULT_PORT, () => {
console.log(`Server running on port ${DEFAULT_PORT}`);
});
server.on('error', (err) => {
if (err.code === 'EADDRINUSE') {
console.error(`Port ${DEFAULT_PORT} is already in use.`);
console.error('Run: kill -9 $(lsof -t -i:' + DEFAULT_PORT + ') then restart.');
process.exit(1);
} else {
throw err;
}
});
Or, if you want your server to automatically find an available port, use the portfinder package:
npm install portfinder
const express = require('express');
const portfinder = require('portfinder');
const app = express();
portfinder.basePort = 3000;
portfinder.getPortPromise()
.then((port) => {
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
})
.catch((err) => {
console.error('Could not find an available port:', err);
process.exit(1);
});
This starts at port 3000 and increments until it finds a free one — useful for running multiple instances side by side.
Step 5: Prevent It From Happening in the First Place
A few habits eliminate this problem almost entirely:
Use nodemon with a proper exit signal. If you’re using nodemon, stop it with Ctrl+C rather than closing the terminal window. Closing the terminal can leave the child process running.
Add a prestart script to your package.json that clears the port automatically:
{
"scripts": {
"prestart": "npx kill-port 3000",
"start": "node server.js"
}
}
Now every time you run npm start, it clears port 3000 first.
Use environment variables for your port, so you can switch ports without touching code:
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Listening on port ${PORT}`));
Then run your server on a different port when needed:
PORT=4000 node server.js
Verification
After killing the process and restarting your server, confirm it’s bound correctly:
lsof -i :3000
You should now see your new Node.js process listed. If the port still shows as occupied after killing the process, wait five seconds and try again — the OS occasionally takes a moment to release the socket.
Takeaways
EADDRINUSEalways means something else claimed the port first — find the PID withlsof(macOS/Linux) ornetstat(Windows), then kill it.- The one-liner
kill -9 $(lsof -t -i:3000)handles the diagnosis and termination in a single step on Unix systems. - Add a
prestartkill script topackage.jsonif this happens frequently on a specific project. - Use
portfinderwhen you need your server to be resilient against port conflicts without manual intervention. - Always use
process.env.PORT— it keeps your code flexible and avoids hardcoded conflicts between projects.
Port conflicts are a minor inconvenience, not a mystery. Once you have these commands memorized, you’ll resolve them in under 30 seconds.