Runtimes Intermediate

Resolving NPM EADDRINUSE: Address Already in Use (Port Busy) on Ubuntu 22.04 LTS

Fix EADDRINUSE errors in Node.js on Ubuntu 22.04 LTS. Learn to identify and terminate processes occupying ports like 3000 or 8080, ensuring your npm applications launch successfully.

👨‍💻
Senior Systems Architect • Verified in Staging Labs

Fix EADDRINUSE errors in Node.js on Ubuntu 22.04 LTS. Learn to identify and terminate processes occupying ports like 3000 or 8080, ensuring your npm applications launch successfully.

When deploying or developing Node.js applications on Ubuntu, encountering an EADDRINUSE error can be a common and frustrating roadblock. This error indicates that the network port your Node.js application attempts to bind to is already in use by another process, preventing your application from starting. This guide will walk you through diagnosing and resolving this port conflict on an Ubuntu 22.04 LTS system.

Symptom & Error Signature

When you attempt to start your Node.js application, typically via npm start, node server.js, or a similar command, the application fails to launch, and your terminal outputs an error message similar to the following:

$ npm run start

> [email protected] start /home/deploy/my-node-app
> node server.js

node:events:505
      throw er; // Unhandled 'error' event
      ^

Error: listen EADDRINUSE: address already in use :::3000
    at Server.setupListenHandle [as _setupListenHandle] (node:net:1481:16)
    at Server.listen (node:net:1569:10)
    at Object.<anonymous> (/home/deploy/my-node-app/server.js:15:5)
    at Module._compile (node:internal/modules/cjs/loader:1275:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1329:10)
    at Module.load (node:internal/modules/cjs/loader:1133:32)
    at Module._load (node:internal/modules/cjs/loader:972:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:83:12)
    at node:internal/main/run_main_module:23:47 {
  code: 'EADDRINUSE',
  errno: -98,
  syscall: 'listen',
  address: '::',
  port: 3000
}

The critical parts of this error message are EADDRINUSE, address already in use, and the port number (e.g., 3000 in the example above). This tells you precisely what the problem is and which port is causing the conflict.

Root Cause Analysis

The EADDRINUSE error occurs when a Node.js application tries to bind to a network port that is already in use by another process. Common scenarios leading to this error include:

  1. Zombie Process / Unclean Shutdown: A previous instance of your Node.js application (or another service) failed to shut down gracefully, leaving its process running in the background and still holding onto the port. This is the most frequent cause during development or after a crash.
  2. Daemonized Application: If your application is managed by a process manager like systemd or PM2, it might still be running as a background service. A simple npm start attempt will conflict with the already running daemon.
  3. Conflicting Service: Another application entirely (e.g., a development web server, a database service, or even another Node.js app) is configured to listen on the same port.
  4. Rapid Restarts (Less Common): In rare cases, extremely rapid restarts of a service can lead to the port being temporarily in a TIME_WAIT state, preventing immediate reuse, though EADDRINUSE specifically indicates an active listener.

Step-by-Step Resolution

Follow these steps to diagnose and resolve the EADDRINUSE error.

1. Identify the Occupying Process

The first step is to determine which process is currently using the problematic port. We'll use lsof (list open files) or ss (socket statistics) for this. Replace <PORT_NUMBER> with the port reported in your error (e.g., 3000).

Using lsof (Recommended):

lsof is often pre-installed or can be easily installed.

sudo apt update
sudo apt install -y lsof # If lsof is not installed
sudo lsof -i :<PORT_NUMBER>

Example Output for Port 3000:

COMMAND     PID   USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
node      12345 deploy    7u  IPv6 123456      0t0  TCP *:3000 (LISTEN)

From this output, you can identify:

  • COMMAND: The name of the process (node).
  • PID: The Process ID (12345).
  • USER: The user running the process (deploy).
  • NAME: The port being listened on (*:3000).

Using ss (Alternative):

ss is part of iproute2 and is usually installed by default on modern Ubuntu systems.

sudo ss -tulnp | grep :<PORT_NUMBER>

Example Output for Port 3000:

tcp   LISTEN 0      4096                       :::3000                    :::*      users:(("node",pid=12345,fd=7))

This output directly shows pid=12345 and node as the command.

2. Terminate the Occupying Process

Once you've identified the PID (e.g., 12345), you have several options to terminate the process.

Option A: Graceful Shutdown (for systemd managed applications)

If your Node.js application is set up as a systemd service (common for production deployments), the correct way to stop it is via systemctl. This ensures a graceful shutdown, allowing the application to clean up resources.

First, check the status to confirm it's running:

sudo systemctl status my-node-app.service

Replace my-node-app.service with the actual name of your service unit file.

Then, stop the service:

sudo systemctl stop my-node-app.service

If you want to prevent it from starting on boot temporarily, you can also disable it:

sudo systemctl disable my-node-app.service
Option B: Kill the Process Directly (for unmanaged or rogue processes)

If the process is not managed by systemd or is a rogue/stuck process, you can use the kill command with the PID you found.

kill 12345 # Replace 12345 with the actual PID

This sends a SIGTERM signal, allowing the process to perform cleanup before exiting. Wait a few seconds to see if it terminates.

If the process persists, you might need to force terminate it with kill -9:

kill -9 12345 # Replace 12345 with the actual PID

Using kill -9 (SIGKILL) forcefully terminates a process immediately without giving it a chance to save data or release resources. Use this as a last resort, especially on critical services, as it can lead to data corruption or instability. Always prefer kill first.

Option C: Using pkill (for known process names)

If you're confident only one Node.js application should be running and you know its name or command, pkill can be convenient.

pkill -f "node server.js" # Kills processes running "node server.js"

Be extremely careful with pkill -f "node" as it will terminate ALL running Node.js processes on your system, which might affect other applications. Always specify the command string as precisely as possible.

3. Verify Port Availability

After attempting to terminate the process, re-run the lsof or ss command from Step 1 to confirm that the port is no longer in use.

sudo lsof -i :<PORT_NUMBER>

If the port is free, this command should return no output (or only the header line).

4. Restart Your Node.js Application

Once you've confirmed the port is free, you can now safely restart your Node.js application.

  • For direct execution:
    npm run start # Or your specific startup command
    
  • For systemd managed applications:
    sudo systemctl start my-node-app.service
    
    You can also check its status:
    sudo systemctl status my-node-app.service
    

5. Implement Permanent Solutions & Best Practices

To prevent EADDRINUSE errors from recurring, consider these long-term strategies:

A. Review systemd Unit Files for Robustness

If you're using systemd, ensure your service unit file (.service) has appropriate restart policies. For example, Restart=on-failure is often more robust than Restart=always if your application can get stuck during startup.

Example systemd unit file (/etc/systemd/system/my-node-app.service):

[Unit]
Description=My Node.js Application
After=network.target

[Service]
User=deploy
WorkingDirectory=/home/deploy/my-node-app
Environment=NODE_ENV=production PORT=3000
ExecStart=/usr/bin/npm start
Restart=on-failure # Only restart if it fails, not if it's explicitly stopped or if it exits cleanly.
RestartSec=5s      # Wait 5 seconds before attempting to restart

[Install]
WantedBy=multi-user.target

After modifying:

sudo systemctl daemon-reload
sudo systemctl enable my-node-app.service
sudo systemctl start my-node-app.service
B. Standardize Port Configuration

Explicitly define your application's port and allow it to be configured via environment variables. This makes it easier to change ports without modifying code.

Example server.js snippet:

const app = require('./app'); // Assuming 'app' exports your Express/Koa app
const port = process.env.PORT || 3000; // Default to 3000, but allow environment override

app.listen(port, () => {
  console.log(`Server listening on port ${port}`);
});

You can then launch your application on a different port:

PORT=8080 npm run start

Or configure it in your systemd unit file as shown above (Environment=PORT=3000).

C. Utilize a Reverse Proxy (e.g., Nginx)

For production environments, running Node.js applications directly on ports like 80 or 443 is generally discouraged. Instead, use a reverse proxy like Nginx to listen on standard web ports (80/443) and forward requests to your Node.js application running on a high, non-standard port (e.g., 3000, 3001, 8080). This centralizes port management and allows multiple applications to share ports 80/443 effectively.

Example Nginx configuration (/etc/nginx/sites-available/myapp.conf):

server {
    listen 80;
    listen [::]:80;
    server_name myapp.example.com;

    location / {
        proxy_pass http://localhost:3000; # Forward to your Node.js app's internal port
        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;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

After creating or modifying the Nginx config:

sudo ln -s /etc/nginx/sites-available/myapp.conf /etc/nginx/sites-enabled/
sudo nginx -t # Test configuration syntax
sudo systemctl restart nginx
D. Containerization with Docker

Docker provides excellent process isolation, ensuring that your application and its dependencies run in a consistent environment. When using Docker, you map host ports to container ports, effectively isolating port usage.

Example Dockerfile:

# ... (your Node.js build steps) ...
EXPOSE 3000 # Your application listens on port 3000 inside the container
CMD ["npm", "start"]

Running the Docker container:

docker build -t my-node-app .
docker run -d -p 80:3000 --name my-app-container my-node-app

Here, 80:3000 maps host port 80 to container port 3000. If host port 80 is busy, Docker will report that specific host port as unavailable, making the error context clearer.

By following these steps, you can effectively diagnose, resolve, and prevent EADDRINUSE port conflicts for your Node.js applications on Ubuntu 22.04 LTS, ensuring smoother deployments and development workflows.

👨‍💻

Johnathon Wheeler

Senior Systems Architect & DevOps Engineer • Austin, TX

Connect on LinkedIn

Johnathon has over 16 years of hands-on experience designing, debugging, and scaling Linux web hosting stacks, container clusters, and high-availability database architectures. Every guide on ButItWorkedLocal is independently tested against Debian 12, Ubuntu 24.04/22.04 LTS, Rocky Linux, and Docker environments to guarantee reproducibility in production.

🛡️

Our Production Verification Guarantee

Encountering a bug not covered here or running a non-standard kernel configuration? Our solutions are continually refined against real production incidents. Submit an environment trace for our editorial team to replicate.