PM2 is a production-grade process manager for Node.js applications. Running a Node.js app directly with node server.js has a critical limitation: if the process crashes, it stays down until someone manually restarts it. PM2 solves this and much more — it automatically restarts crashed processes, starts applications on system boot, enables zero-downtime reloads for application updates, manages multiple applications, aggregates logs from all processes, and provides built-in cluster mode that forks the application across all CPU cores to fully utilise multi-core servers. This guide covers deploying a Node.js application with PM2 on RHEL 9, configuring the PM2 ecosystem file, enabling startup on boot, and using cluster mode for horizontal scaling.

Prerequisites

  • Node.js installed on RHEL 9
  • A Node.js application ready to deploy

Step 1 — Install PM2

npm install -g pm2
pm2 --version

Step 2 — Start an Application with PM2

cd /var/www/myapp

# Start the application
pm2 start server.js --name myapp

# Start with cluster mode (uses all CPU cores)
pm2 start server.js --name myapp --instances max

# Start a TypeScript application
pm2 start npm --name myapp -- start

# View running processes
pm2 list
pm2 show myapp

Step 3 — Create an Ecosystem Configuration File

# ecosystem.config.js — PM2 configuration file
module.exports = {
    apps: [
        {
            name: 'myapp',
            script: 'server.js',
            instances: 'max',          // Use all CPU cores
            exec_mode: 'cluster',
            watch: false,              // Disable in production
            max_memory_restart: '512M', // Restart if memory exceeds 512 MB
            env: {
                NODE_ENV: 'development',
                PORT: 3000
            },
            env_production: {
                NODE_ENV: 'production',
                PORT: 3000
            },
            error_file: '/var/log/pm2/myapp-error.log',
            out_file: '/var/log/pm2/myapp-out.log',
            log_date_format: 'YYYY-MM-DD HH:mm:ss',
            merge_logs: true
        }
    ]
};
# Start using the ecosystem file
pm2 start ecosystem.config.js --env production

Step 4 — Enable Startup on Boot

# Generate and configure the startup script
pm2 startup systemd
# PM2 outputs a command to run — execute it, e.g.:
# sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u myuser --hp /home/myuser

# Save the current process list to restore on reboot
pm2 save

# Verify
systemctl status pm2-root  # or pm2-

Step 5 — Zero-Downtime Reload and Monitoring

# Zero-downtime reload (cluster mode only — new code loaded without dropping connections)
pm2 reload myapp

# Hard restart (brief downtime)
pm2 restart myapp

# Stop/delete
pm2 stop myapp
pm2 delete myapp

# Real-time monitoring dashboard
pm2 monit

# View logs
pm2 logs myapp --lines 100

# Flush logs
pm2 flush

Step 6 — Nginx Reverse Proxy for PM2 Application

# /etc/nginx/conf.d/myapp.conf
upstream nodeapp {
    server 127.0.0.1:3000;
    keepalive 64;
}

server {
    listen 80;
    server_name myapp.example.com;

    location / {
        proxy_pass http://nodeapp;
        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;
    }
}

Conclusion

PM2 on RHEL 9 is the production standard for Node.js process management. Cluster mode with instances: 'max' is the single most impactful performance optimisation for CPU-bound Node.js applications — it turns a single-threaded process into a multi-process pool that uses all available CPU cores. Zero-downtime reload (pm2 reload) deploys application updates without dropping any existing connections, making it possible to update Node.js applications without any planned maintenance windows.

Next steps: How to Install Node.js on RHEL 9, How to Install and Configure Nginx on RHEL 9, and How to Monitor Node.js Applications on RHEL 9.