If you've worked with Node.js long enough, you've probably seen this error:
Error: listen EADDRINUSE: address already in use :::3000
Or:
Error: listen EADDRINUSE: address already in use 0.0.0.0:5000
Your application refuses to start because the port you're trying to bind is already occupied.
This is one of the most common issues developers encounter during local development and deployments.
Let's fix it properly.
Problem
You start your Node.js server:
node server.js
Instead of launching successfully, Node crashes:
Error: listen EADDRINUSE: address already in use :::3000
At this point, only one process can listen on a specific port.
If another process already owns that port, Node throws the EADDRINUSE error and exits.
Why It Happens
In production and development environments, this usually happens for one of these reasons:
1. Another Node.js process is already running
You accidentally started the application twice.
Example:
node server.js
Then in another terminal:
npm run dev
Both attempt to bind to port 3000.
2. A crashed process didn't release the port
This happens surprisingly often during development.
You close the terminal but the process remains alive in the background.
The port stays occupied.
3. Another application is using the port
Common examples:
- Docker containers
- PostgreSQL
- Redis
- Nginx
- Another microservice
- Local development tools
4. Multiple containers mapped to the same host port
Docker example:
ports:
- "3000:3000"
Two containers trying to expose port 3000 on the host will conflict.
Example
Consider a simple Express application:
const express = require("express");
const app = express();
app.listen(3000, () => {
console.log("Server running");
});
Start it:
node app.js
Now start it again in another terminal:
node app.js
Result:
Error: listen EADDRINUSE: address already in use :::3000
The first process already owns the port.
Production Ready Solution
Step 1: Identify Which Process Owns the Port
Linux / macOS
Find the process:
lsof -i :3000
Example output:
COMMAND PID USER
node 8234 dev
Alternative
sudo netstat -tulpn | grep 3000
Windows
netstat -ano | findstr :3000
Output:
TCP 0.0.0.0:3000 LISTENING 12345
Locate the process:
tasklist | findstr 12345
Step 2: Kill the Process
Linux / macOS
kill -9 8234
Windows
taskkill /PID 12345 /F
The port becomes available immediately.
Step 3: Handle the Error Gracefully
Production applications should not crash without useful logs.
Example:
const PORT = process.env.PORT || 3000;
const server = app.listen(PORT, () => {
console.log(`Running on ${PORT}`);
});
server.on("error", (err) => {
if (err.code === "EADDRINUSE") {
console.error(`Port ${PORT} already in use`);
process.exit(1);
}
throw err;
});
This makes troubleshooting much easier.
Step 4: Use Environment Variables
Hardcoding ports causes unnecessary conflicts.
Bad:
app.listen(3000);
Better:
const PORT = process.env.PORT || 3000;
app.listen(PORT);
Run:
PORT=5000 node app.js
This becomes especially useful when running multiple services locally.
Step 5: Auto-Detect an Available Port
For internal tools and development environments:
const getPort = require("get-port");
(async () => {
const port = await getPort({
port: 3000
});
app.listen(port);
})();
The application automatically chooses an available port.
Common Mistakes
Killing the Wrong Process
Many developers immediately run:
kill -9 <pid>
Without verifying what owns the port.
Always inspect first.
I've seen developers accidentally terminate databases, Redis instances, and even production services on shared servers.
Assuming the Port Is Free
Just because a terminal was closed doesn't mean the process stopped.
Always verify:
lsof -i :3000
before making assumptions.
Hardcoding Ports Everywhere
Large projects often contain:
3000
5000
8000
8080
scattered throughout the codebase.
Use environment variables instead.
Ignoring Docker Port Mapping
A common issue:
service-a:
ports:
- "3000:3000"
service-b:
ports:
- "3000:3000"
Docker won't allow both containers to bind the same host port.
Assign unique host ports.
Debugging Tips
When EADDRINUSE appears repeatedly, I usually follow this checklist.
Verify the owning process
lsof -i :3000
Check background Node processes
ps aux | grep node
You'll often find forgotten development servers.
Check Docker containers
docker ps
Look for containers exposing the conflicting port.
Check PM2
If you're using PM2:
pm2 list
A previously deployed process may still be running.
Check startup scripts
Sometimes systemd, PM2, Docker Compose, or Kubernetes automatically restart processes after they're killed.
The port immediately becomes occupied again.
Verify what's managing the service.
Performance Considerations
Repeatedly spawning and killing processes during development can leave orphaned processes consuming memory and CPU.
A few recommendations:
- Use process managers responsibly
- Avoid multiple watchers running simultaneously
- Shut down servers gracefully
- Monitor active processes regularly
For Express applications, graceful shutdown looks like this:
process.on("SIGTERM", () => {
server.close(() => {
console.log("Server closed");
process.exit(0);
});
});
This helps release ports cleanly.
Final Thoughts
EADDRINUSE isn't really a Node.js problem.
It's an operating system telling you that another process already owns the network port.
The fastest way to solve it is:
- Identify the process
- Verify it's safe to stop
- Kill it if necessary
- Use configurable ports
- Add proper error handling
Those five steps eliminate most port-related startup issues in both local development and production environments.
If you're building backend systems regularly, keeping a small troubleshooting reference for common Node.js errors can save hours over time. I maintain similar engineering notes across projects and occasionally publish deeper backend debugging guides on NileshBlog.Tech and TechNilesh when a production issue is worth documenting.
Top comments (0)