DEV Community

Cover image for How to Fix EADDRINUSE: Port Already in Use
Nilesh Raut
Nilesh Raut

Posted on

How to Fix EADDRINUSE: Port Already in Use

If you've worked with Node.js long enough, you've probably seen this error:

Error: listen EADDRINUSE: address already in use :::3000
Enter fullscreen mode Exit fullscreen mode

Or:

Error: listen EADDRINUSE: address already in use 0.0.0.0:5000
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Instead of launching successfully, Node crashes:

Error: listen EADDRINUSE: address already in use :::3000
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Then in another terminal:

npm run dev
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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");
});
Enter fullscreen mode Exit fullscreen mode

Start it:

node app.js
Enter fullscreen mode Exit fullscreen mode

Now start it again in another terminal:

node app.js
Enter fullscreen mode Exit fullscreen mode

Result:

Error: listen EADDRINUSE: address already in use :::3000
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Example output:

COMMAND   PID USER
node     8234 dev
Enter fullscreen mode Exit fullscreen mode

Alternative

sudo netstat -tulpn | grep 3000
Enter fullscreen mode Exit fullscreen mode

Windows

netstat -ano | findstr :3000
Enter fullscreen mode Exit fullscreen mode

Output:

TCP    0.0.0.0:3000    LISTENING    12345
Enter fullscreen mode Exit fullscreen mode

Locate the process:

tasklist | findstr 12345
Enter fullscreen mode Exit fullscreen mode

Step 2: Kill the Process

Linux / macOS

kill -9 8234
Enter fullscreen mode Exit fullscreen mode

Windows

taskkill /PID 12345 /F
Enter fullscreen mode Exit fullscreen mode

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;
});
Enter fullscreen mode Exit fullscreen mode

This makes troubleshooting much easier.


Step 4: Use Environment Variables

Hardcoding ports causes unnecessary conflicts.

Bad:

app.listen(3000);
Enter fullscreen mode Exit fullscreen mode

Better:

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

app.listen(PORT);
Enter fullscreen mode Exit fullscreen mode

Run:

PORT=5000 node app.js
Enter fullscreen mode Exit fullscreen mode

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);
})();
Enter fullscreen mode Exit fullscreen mode

The application automatically chooses an available port.


Common Mistakes

Killing the Wrong Process

Many developers immediately run:

kill -9 <pid>
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

before making assumptions.


Hardcoding Ports Everywhere

Large projects often contain:

3000
5000
8000
8080
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Check background Node processes

ps aux | grep node
Enter fullscreen mode Exit fullscreen mode

You'll often find forgotten development servers.


Check Docker containers

docker ps
Enter fullscreen mode Exit fullscreen mode

Look for containers exposing the conflicting port.


Check PM2

If you're using PM2:

pm2 list
Enter fullscreen mode Exit fullscreen mode

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);
  });
});
Enter fullscreen mode Exit fullscreen mode

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:

  1. Identify the process
  2. Verify it's safe to stop
  3. Kill it if necessary
  4. Use configurable ports
  5. 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)