Installing Node.js and PM2 on CentOS Stream 9: Secrets to Keeping Your App Alive at 2 AM

CentOS tutorial - IT technology blog
CentOS tutorial - IT technology blog

The 502 Bad Gateway Nightmare and a Real-World Lesson

My phone vibrated violently at 2 AM. On the other end was my boss, sounding frustrated: “The website is down; customers are getting 502 errors during checkout.” I scrambled to my computer and SSH’d into the CentOS Stream 9 server. Running ps aux | grep node returned nothing.

The issue was elementary: Last night, I ran npm start and then closed the terminal to go to sleep. When the SSH session ended, CentOS automatically scanned and cleaned up child processes. As a result, the application vanished instantly. Or, just one small uncaughtException could cause Node.js to crash and stay down, waiting for someone to rescue it.

After migrating five servers from CentOS 8 to CentOS Stream 9, I realized: If you want to sleep soundly, never run Node.js directly in production. You need a reliable Process Manager. That is why PM2 exists.

Why Does Your Node.js App Keep Disappearing?

Node.js operates on a single-threaded mechanism. Just one unhandled logic error can stop the entire process. In a dev environment, you can manually restart it. But in production, you can’t monitor the server 24/7.

Here are four “villains” that frequently kill Node.js apps on CentOS:

  • Session Timeout: Closing the terminal window means terminating the process.
  • Runtime Errors: Buggy code causes the process to crash unexpectedly.
  • Server Reboot: Cloud provider maintenance or kernel updates cause the server to restart.
  • OOM (Out of Memory): On low-RAM VPS (e.g., 1GB plans), an app consuming too many resources will be killed by the system to protect the OS.

PM2: A Professional Alternative to nohup and Systemd

Many beginners use nohup node app.js &. This keeps the app running after closing the terminal, but it’s extremely difficult to manage logs. Others use Systemd to create .service files. This is the standard Linux way, but configuring a new file every time you add an app is quite tedious.

PM2 (Process Manager 2) is the optimal choice. It provides everything: automatic restart on crash, centralized log management, and real-time resource monitoring with just a few simple commands.

Installing Node.js on CentOS Stream 9: Choose LTS for Stability

CentOS Stream 9 manages software via AppStream. Instead of installing the old version in the default repo, choose the Long Term Support (LTS) version to ensure your libraries run as smoothly as possible.

Step 1: Check Available Node.js Versions

sudo dnf module list nodejs

The system will list streams like 16, 18, and 20. I recommend choosing Node.js 20. It is a modern LTS version with the best support for frameworks like Next.js or NestJS.

Step 2: Enable and Install

# Choose the version 20 stream
sudo dnf module enable nodejs:20 -y

# Install nodejs and npm
sudo dnf install nodejs -y

# Verify the version
node -v

PM2 – The Dedicated “Bodyguard” for Your Application

Once you have Node.js, install PM2 globally to control the system from anywhere.

sudo npm install pm2 -g

Launching Your First Application

Suppose your project is located at /var/www/api-service/index.js. Instead of using the standard node command, let PM2 take over:

cd /var/www/api-service
pm2 start index.js --name "api-production"

At this point, your app is safe. Even if you log out of SSH, the application continues to serve users silently.

Quick Commands for Troubleshooting

During operation, I often use the following commands to handle issues:

  • pm2 list: Check how much RAM and CPU the app is consuming.
  • pm2 logs api-production: View live logs. Very useful for finding the cause of 500 errors.
  • pm2 restart api-production: Restart the app after you’ve updated the code.

Configuring Auto-start on System Boot

This is the most important step that many people skip. If the server reboots, PM2 won’t run again automatically unless you set up a Startup Script.

First, ask PM2 to generate a startup command for systemd:

pm2 startup

The system will return a long command starting with sudo env PATH=.... You need to copy that entire line, paste it into the terminal, and press Enter.

Finally, save the current state so PM2 remembers which apps are running:pm2 save

Now, you can try sudo reboot. Once the server finishes booting, your app will “resurrect” automatically without any manual intervention.

Opening Firewall Ports on CentOS Stream 9

If the app is running on port 3000 but the browser cannot access it, firewalld is likely blocking the path. Open the port using these commands:

sudo firewall-cmd --permanent --add-port=3000/tcp
sudo firewall-cmd --reload

Advanced Tip: Leveraging Cluster Mode

By default, Node.js runs on only one CPU core. If your server has 4 cores, you are wasting 75% of your hardware power. PM2 features Cluster Mode, which allows you to run multiple instances simultaneously:

# Maximize the usage of available CPU cores
pm2 start index.js -i max

With this configuration, if one instance crashes, other instances still handle requests as usual. This helps the system achieve near Zero Downtime.

Managing Node.js on CentOS Stream 9 becomes effortless if you set up the right process from the start. Hopefully, these real-world experiences will help your system stay resilient against any traffic spikes.

Share: