Resolving NPM EADDRINUSE: Address Already In Use Port Errors on CentOS Stream / Rocky Linux

Fix 'NPM EADDRINUSE' errors on CentOS Stream or Rocky Linux when a Node.js process attempts to bind to an already-occupied port. Identify and terminate rogue processes effectively.


Fix 'NPM EADDRINUSE' errors on CentOS Stream or Rocky Linux when a Node.js process attempts to bind to an already-occupied port. Identify and terminate rogue processes effectively.

When attempting to start a Node.js application, especially in a server environment like CentOS Stream or Rocky Linux, encountering the EADDRINUSE error can be a common roadblock. This error indicates that the network port your Node.js application is configured to listen on is already in use by another process. This guide provides a systematic, highly technical approach to diagnose and resolve this issue, ensuring your Node.js services are up and running smoothly.

Symptom & Error Signature

Typically, when you attempt to start your Node.js application using npm start, node server.js, or via a process manager like PM2 or Systemd, the operation will fail immediately with an output similar to one of the following:

$ npm start

> [email protected] start /path/to/my-node-app
> node server.js

(node:12345) UnhandledPromiseRejectionWarning: Error: listen EADDRINUSE: address already in use :::3000
    at Server.setupListenHandle [as _setupListenHandle] (net.js:1316:16)
    at Server.listen (net.js:1414:10)
    at Object.<anonymous> (/path/to/my-node-app/server.js:20:8)
    at Module._compile (internal/modules/cjs/loader.js:1072:14)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1101:10)
    at Module.load (internal/modules/cjs/loader.js:937:32)
    at Function.Module._load (internal/modules/cjs/loader.js:778:12)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:76:12)
    at internal/main/run_main_module.js:17:47
(node:12345) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process, use the "kill -9 <PID>" command.
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-07-25T12_00_00_000Z-debug-0.log

The key indicator is Error: listen EADDRINUSE: address already in use :::3000 (where 3000 is the conflicting port).

Root Cause Analysis

The EADDRINUSE error fundamentally means that the operating system's kernel cannot bind a socket to a requested address and port because another process already holds a listener on that specific network endpoint. The primary reasons for this on a Linux server are:

  1. Stale/Zombie Process: A previous instance of your Node.js application (or another service) did not shut down cleanly. It might have crashed or been terminated abruptly, leaving its process running in the background and still holding the port.
  2. Conflicting Service: Another application or service is legitimately configured to use the same port. This could be another web server (e.g., Nginx, Apache), a database, a monitoring agent, or even another Node.js application.
  3. Rapid Restarts/Restart Loops: If a Node.js application is configured to restart immediately upon failure (e.g., via a Systemd unit with a Restart=always policy), and it fails to unbind the port quickly enough, a subsequent restart attempt might run into the EADDRINUSE error.
  4. Misconfiguration: The application itself or its environment configuration specifies a port that is known to be in use by a critical system service or another application.

Step-by-Step Resolution

The resolution involves identifying the process holding the port, verifying its legitimacy, and then gracefully or forcefully terminating it.

1. Identify the Process Using the Port

The first step is to determine which process is currently listening on the port your Node.js application is trying to use.

# Replace 3000 with your application's port
PORT=3000

# Use ss (recommended on modern Linux)
sudo ss -tulpn | grep ":$PORT"

# Alternatively, use netstat (if ss is unavailable or for familiarity)
# If netstat is not installed: sudo dnf install net-tools
# sudo netstat -tulpn | grep ":$PORT"

Example Output:

tcp    LISTEN     0      511    0.0.0.0:3000       0.0.0.0:*    users:(("node",pid=12345,fd=18))

From this output, we can deduce:

  • pid=12345: The Process ID (PID) of the application.
  • "node": The name of the executable (indicating it's a Node.js process).
  • 0.0.0.0:3000: The process is listening on all available network interfaces on port 3000.

2. Verify the Process and its Owner

Once you have the PID, it's crucial to verify that it's indeed the process you intend to terminate, especially in a multi-user or shared hosting environment.

# Replace 12345 with the PID identified in the previous step
PID=12345

# Get detailed information about the process
ps aux | grep "$PID" | grep -v "grep"

Example Output:

user1      12345  0.1  0.8 123456 78900 ?        Sl   Jul24   0:05 node /path/to/my-node-app/server.js

This output confirms that PID 12345 is a node process owned by user1 and running /path/to/my-node-app/server.js. This helps confirm it's a rogue instance of your application.

Always verify the PID before terminating any process. Incorrectly killing a process can lead to system instability, data corruption, or service outages, especially if it's a critical system service or another legitimate application.

3. Terminate the Conflicting Process

With the PID confirmed, you can now terminate the process. Start with a graceful termination, then resort to a forceful kill if necessary.

3.1. Graceful Termination (SIGTERM)

A SIGTERM (signal 15) requests the process to shut down gracefully, allowing it to clean up resources, close connections, and save state.

sudo kill "$PID"

After sending SIGTERM, wait a few seconds (e.g., 5-10 seconds) and check if the process has terminated:

sudo ss -tulpn | grep ":$PORT"
# Or
ps aux | grep "$PID" | grep -v "grep"

If the process is no longer listed, it shut down successfully. Proceed to start your Node.js application.

3.2. Forceful Termination (SIGKILL)

If the process persists after SIGTERM, it might be stuck or unresponsive. In such cases, SIGKILL (signal 9) will forcefully terminate the process immediately, without giving it a chance to clean up.

SIGKILL should be used as a last resort. It's equivalent to pulling the power plug and can leave resources in an inconsistent state, though for stateless Node.js applications, this is often acceptable.

sudo kill -9 "$PID"

Verify termination again using ss or ps.

4. Restart Your Node.js Application

Once the conflicting process is confirmed to be terminated, you can restart your Node.js application.

4.1. For applications managed by npm or node:
cd /path/to/my-node-app
npm start
# Or
node server.js
4.2. For applications managed by Systemd:

If your Node.js application runs as a Systemd service (highly recommended for production environments on CentOS Stream / Rocky Linux):

sudo systemctl start my-node-app.service
sudo systemctl status my-node-app.service

If your application is managed by Systemd, ensure its .service file is correctly configured. A common issue is not having Type=simple or Type=forking and ExecStart pointing correctly. Also, consider Restart=always with RestartSec=5s to automatically recover from crashes, but be mindful of restart loops if the underlying issue isn't fixed.

Example Systemd Unit File (/etc/systemd/system/my-node-app.service):

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

[Service]
User=myuser # Replace with the user running the app
Group=myuser # Replace with the group
WorkingDirectory=/path/to/my-node-app
Environment=NODE_ENV=production
ExecStart=/usr/bin/node /path/to/my-node-app/server.js
# Or if using npm: ExecStart=/usr/bin/npm start
Restart=always
RestartSec=5s # Wait 5 seconds before restarting
TimeoutStopSec=10 # Give 10 seconds for graceful shutdown

[Install]
WantedBy=multi-user.target

After creating or modifying a Systemd unit file:

sudo systemctl daemon-reload
sudo systemctl enable my-node-app.service
sudo systemctl start my-node-app.service
sudo systemctl status my-node-app.service

5. Persistent Solutions and Best Practices

To prevent future EADDRINUSE errors:

  • Review Application Shutdown Logic: Ensure your Node.js application handles SIGTERM (e.g., using process.on('SIGTERM', ...) or libraries like terminator) to shut down gracefully and release ports when exiting.
  • Use Process Managers: Tools like PM2 or Systemd are designed to manage application lifecycle, including graceful restarts and recovery from crashes, minimizing the chances of stale processes.
  • Unique Ports: Ensure all applications deployed on the same server use unique ports. Avoid hardcoding ports; use environment variables (e.g., PORT=3000 node server.js) or configuration files.
  • Check Firewall Rules: While EADDRINUSE is not typically a firewall issue (it means the port is locally busy, not blocked externally), ensure your firewall (firewalld on CentOS Stream/Rocky Linux) allows inbound connections to your application's port if it's meant to be publicly accessible.
    sudo firewall-cmd --permanent --add-port=3000/tcp
    sudo firewall-cmd --reload
    
  • Address High TIME_WAIT States: In extremely high-traffic scenarios, if many connections are being closed rapidly, the port might be temporarily unavailable due to sockets being in a TIME_WAIT state. While EADDRINUSE usually indicates an active listener, if you suspect TIME_WAIT issues, you can temporarily adjust kernel parameters (e.g., net.ipv4.tcp_tw_reuse, net.ipv4.tcp_tw_recyclenote: tcp_tw_recycle is generally discouraged due to NAT issues). This is an advanced topic and usually not the direct cause of EADDRINUSE.

By following these steps, you can effectively troubleshoot and resolve NPM EADDRINUSE errors, ensuring high availability and reliability for your Node.js applications on CentOS Stream and Rocky Linux.