How to Fix ‘Port Already in Use’ Errors in Node.js

How to Fix ‘Port Already in Use’ Errors in Node.js

Port Already in Use — Node.js debugging

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 nodemon or pm2 restart 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

  • EADDRINUSE always means something else claimed the port first — find the PID with lsof (macOS/Linux) or netstat (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 prestart kill script to package.json if this happens frequently on a specific project.
  • Use portfinder when 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.

Leave a Reply

Your email address will not be published. Required fields are marked *.

*
*