Troubleshooting NPM EADDRINUSE: Address Already in Use (Port Busy) for Node.js Applications

Fix 'EADDRINUSE' errors when your Node.js app fails to start because a port is already in use. Learn to identify and terminate conflicting processes.


When deploying or restarting a Node.js application, encountering the EADDRINUSE error signifies that your application is attempting to bind to a network port that is already actively in use by another process. This prevents your Node.js server from starting up, leading to service downtime or development frustration. This guide will walk you through identifying the root cause and implementing robust solutions in typical hosting environments.

Symptom & Error Signature

The primary symptom is your Node.js application failing to start, often accompanied by a stack trace that explicitly mentions EADDRINUSE. You will typically see this error in your terminal during development, in application logs, or in Systemd journal output if your application is managed as a service.

Typical error output from a Node.js application trying to start on an occupied port (e.g., port 3000):

Error: listen EADDRINUSE: address already in use :::3000
    at Server.setupListenHandle [as _listen2] (node:net:1803:16)
    at listenInCluster (node:net:1851:12)
    at Server.listen (node:net:1936:7)
    at Function.listen (/path/to/your/app/node_modules/express/lib/application.js:618:24)
    at Object.<anonymous> (/path/to/your/app/server.js:25:5)
    at Module._compile (node:internal/modules/cjs/loader:1105:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
    at Module.load (node:internal/modules/cjs/loader:981:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12) {
  code: 'EADDRINUSE',
  errno: -98,
  syscall: 'listen',
  address: '::',
  port: 3000
}

If you are using npm run start, the output might be wrapped with npm’s error messages:

> [email protected] start
> node server.js

Error: listen EADDRINUSE: address already in use :::3000
    at Server.setupListenHandle [as _listen2] (node:net:1803:16)
    ... (similar stack trace as above)
code: 'EADDRINUSE', errno: -98, syscall: 'listen', address: '::', port: 3000
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] start: `node server.js`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/user/.npm/_logs/2026-06-27T12_00_00_000Z-debug.log

Root Cause Analysis

The EADDRINUSE error occurs when a process attempts to bind to an IP address and port combination that is already in use by another process on the same system. The common underlying reasons include:

  • Lingering Process: A previous instance of your Node.js application or another service failed to shut down gracefully and is still holding onto the port. This is perhaps the most common cause.
  • Another Application Conflict: A different application (e.g., another web server like Nginx or Apache, a database, a monitoring agent, or another Node.js instance) is intentionally or unintentionally configured to listen on the same port.
  • Rapid Restarts: If a service is configured to restart very quickly (e.g., by Systemd), the operating system might not have had enough time to fully release the port from its TIME_WAIT state before the new instance tries to bind to it.
  • Docker Port Mapping Conflicts: If your application is containerized, a Docker container might be mapping a host port that is already in use by another container or a host process, or a previous container instance might not have been properly stopped and removed.
  • Incorrect Configuration: A misconfiguration in your application, process manager, or Systemd unit file might cause multiple instances to be launched, all attempting to claim the same port.

Step-by-Step Resolution

Follow these steps to diagnose and resolve the EADDRINUSE error.

1. Identify the Process Using the Port

The first step is to determine which process is currently occupying the port your Node.js application wants to use. We’ll use command-line tools lsof or netstat/ss.

Assuming your Node.js app is trying to bind to port 3000:

# Using lsof (List Open Files - excellent for process details)
sudo lsof -i :3000

# Example output indicating a Node.js process (PID 12345) using port 3000:
# COMMAND     PID   USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
# node      12345   user   21u  IPv6 123456      0t0  TCP *:3000 (LISTEN)

If lsof is not available, you can use netstat or ss:

# Using netstat (Network Statistics)
sudo netstat -tulnp | grep :3000

# Example output (PID is usually in the last column with netstat -p):
# tcp6       0      0 :::3000                 :::*                    LISTEN      12345/node
# Using ss (Socket Statistics - modern replacement for netstat)
sudo ss -tulnp | grep :3000

# Example output (PID and process name are typically within 'users' column):
# tcp    LISTEN     0      128       :::3000            :::*          users:(("node",pid=12345,fd=21))

[!TIP] If lsof, netstat, or ss are not installed on your Ubuntu/Debian system, you can install them: sudo apt update && sudo apt install lsof net-tools iproute2

2. Terminate the Conflicting Process

Once you’ve identified the Process ID (PID) from the previous step (e.g., 12345), you can terminate it.

# Terminate the process gracefully (SIGTERM)
kill 12345

Wait a few seconds and then try starting your Node.js application again. If the process is stubborn and doesn’t terminate, you may need to force-kill it:

# Forcefully terminate the process (SIGKILL)
kill -9 12345

[!WARNING] Using kill -9 (SIGKILL) should be a last resort. It immediately terminates the process without allowing it to clean up resources, which can sometimes lead to data corruption or orphaned resources. Always try kill (SIGTERM) first.

3. Manage Node.js Applications with Systemd

If your Node.js application is managed by systemd (which is common in production environments), the EADDRINUSE error might stem from improper service stop/restart procedures or misconfiguration.

  1. Check Service Status:

    sudo systemctl status my-app.service

    This will show if the service is active, restarting, or failed, along with recent logs.

  2. Stop the Service: If the service is active when it shouldn’t be, try stopping it cleanly:

    sudo systemctl stop my-app.service

    Verify it has stopped using systemctl status and then retry your Node.js app or systemctl start my-app.service.

  3. Reload and Restart: If you’ve made changes to the Systemd unit file (e.g., /etc/systemd/system/my-app.service), you need to reload the daemon and then restart the service:

    sudo systemctl daemon-reload
    sudo systemctl restart my-app.service
  4. Review Systemd Unit File Configuration: Inspect your service file for common issues:

    # Example: /etc/systemd/system/my-app.service
    [Unit]
    Description=My Node.js Application
    After=network.target
    
    [Service]
    ExecStart=/usr/bin/node /opt/my-app/server.js
    WorkingDirectory=/opt/my-app
    Restart=always
    RestartSec=5
    User=www-data
    Environment=NODE_ENV=production PORT=3000 # Ensure PORT is correct
    
    [Install]
    WantedBy=multi-user.target
    • Restart=always: Ensures the service restarts if it crashes. Combined with RestartSec, it can cause EADDRINUSE if RestartSec is too short.
    • RestartSec=5: Sets a 5-second delay before restarting. Consider increasing this value if you suspect rapid restarts are the issue.
    • ExecStart: Verify the path to node and your application’s main file is correct.
    • User: Ensure the user has permissions to the WorkingDirectory.
    • Environment=PORT=3000: Explicitly sets the port. Make sure this matches your application’s intended port.

[!IMPORTANT] If a Systemd service is configured with Restart=always and RestartSec is very low (e.g., 1 second), a crash might lead to a quick restart that attempts to rebind the port before the OS fully releases it from the previous instance, causing EADDRINUSE in a loop. Adjust RestartSec to 5s or 10s if this is the case.

4. Resolve Docker Container Port Conflicts

If your Node.js application runs within a Docker container, the EADDRINUSE error often points to port mapping issues or lingering container instances.

  1. List Running Containers: Check which containers are running and their exposed ports:

    docker ps

    Look for any container that is mapping the problematic port (e.g., 0.0.0.0:3000->3000/tcp).

  2. Stop and Remove Conflicting Containers: If you find a container that shouldn’t be running or is occupying the port:

    # Replace <container_id> with the actual ID from 'docker ps'
    docker stop <container_id>
    docker rm <container_id>
  3. Check for All Containers (including stopped ones): Sometimes, a stopped container might still hold onto resources (less common for EADDRINUSE, but good to check).

    docker ps -a

    If you see many exited containers, consider pruning them.

  4. Prune Unused Docker Objects: To clean up all stopped containers, unused networks, dangling images, and build cache:

    docker system prune -f

    [!CAUTION] docker system prune -f is a destructive operation. It will remove all stopped containers and other unused Docker resources. Use with caution in production, ensuring you don’t remove anything critical.

  5. Review Dockerfile/docker-compose: Ensure your Docker configuration correctly maps ports and that there are no accidental overlaps.

    • Dockerfile: EXPOSE 3000 (declares the port, doesn’t publish)
    • docker run: -p 3000:3000 (maps host port 3000 to container port 3000)
    • docker-compose.yml:
      services:
        my-app:
          image: my-node-app
          ports:
            - "3000:3000" # Host_port:Container_port

    Ensure the Host_port (left side of the colon) is unique or available on the host machine.

5. Modify Application Port

If the port conflict is a recurring issue, or you need to run multiple services on the same host, the simplest solution might be to change the port your Node.js application listens on.

  1. In your Node.js application code: Modify the port number in your application’s entry file (e.g., server.js, app.js). It’s best practice to use an environment variable.

    const express = require('express');
    const app = express();
    const port = process.env.PORT || 4000; // Changed default from 3000 to 4000
    
    app.get('/', (req, res) => {
      res.send('Hello World!');
    });
    
    app.listen(port, () => {
      console.log(`App listening at http://localhost:${port}`);
    });
  2. Update Systemd service file (if applicable): If using Systemd, update the Environment variable:

    # In /etc/systemd/system/my-app.service
    Environment=NODE_ENV=production PORT=4000

    After modification: sudo systemctl daemon-reload && sudo systemctl restart my-app.service

  3. Update Dockerfile/docker-compose (if applicable): If running in Docker, update the port mapping:

    # In docker-compose.yml
    ports:
      - "4000:4000" # Map host port 4000 to container port 4000

    Or for docker run: -p 4000:4000

  4. Update Nginx Configuration (if applicable): If Nginx is acting as a reverse proxy for your Node.js application, you must update its configuration to reflect the new backend port.

    # Example: /etc/nginx/sites-available/my-app.conf
    server {
        listen 80;
        server_name myapp.example.com;
    
        location / {
            proxy_pass http://localhost:4000; # Changed to port 4000
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
        }
    }

    After modifying Nginx configuration, test it for syntax errors and reload the service:

    sudo nginx -t
    sudo systemctl reload nginx

6. Utilize a Process Manager (PM2)

For production Node.js applications, using a dedicated process manager like PM2 is highly recommended. PM2 simplifies starting, stopping, restarting, and monitoring Node.js processes, significantly reducing the chances of EADDRINUSE due to orphaned processes.

  1. Install PM2 globally:

    npm install -g pm2
  2. Start your application with PM2:

    # Start your main application file (e.g., server.js) and give it a name
    pm2 start server.js --name my-app
  3. Check PM2 status:

    pm2 list
  4. Save PM2 process list and generate Systemd startup script: PM2 can automatically generate a Systemd unit file to ensure PM2 itself starts on boot and manages your applications.

    pm2 save # Saves the current process list so PM2 can restore it
    pm2 startup systemd # Generates and provides instructions for the Systemd script

    Follow the instructions provided by pm2 startup to copy and enable the generated Systemd service file. This creates a robust setup where Systemd manages PM2, and PM2, in turn, manages your Node.js applications, handling restarts and resource management effectively.