How to Deploy a Node.js Application with PM2 on RHEL 7

PM2 is a battle-tested production process manager for Node.js applications. It keeps your application running after a crash by restarting it automatically, provides integrated logging, enables zero-downtime reloads, supports cluster mode to utilize all CPU cores, and integrates with systemd for startup on boot. Whether you are deploying a single Express API or a suite of microservices, PM2 simplifies the operational side of Node.js hosting. This tutorial covers installing PM2, starting and managing applications from the command line, configuring systemd integration, using an ecosystem configuration file for multi-application deployments, monitoring resource usage, and setting up automatic log rotation on RHEL 7.

Prerequisites

  • RHEL 7 server with root or sudo access
  • Node.js and npm installed (see the Node.js installation guide)
  • A Node.js application to deploy (e.g., an Express server)
  • systemd available (default on RHEL 7)

Step 1: Install PM2 Globally

Install PM2 as a global npm package so it is available system-wide as the pm2 command:

sudo npm install -g pm2

If you installed Node.js via nvm and prefer to install without sudo:

npm install -g pm2

Verify the installation:

pm2 --version

Step 2: Create a Sample Application

For this tutorial, create a minimal Express application to use as the deployment target:

mkdir -p ~/myapp && cd ~/myapp
npm init -y
npm install express

cat > app.js <<'EOF'
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

app.use(express.json());

app.get('/', (req, res) => {
    res.json({
        status: 'running',
        pid: process.pid,
        uptime: process.uptime()
    });
});

app.get('/health', (req, res) => {
    res.json({ healthy: true });
});

app.listen(PORT, () => {
    console.log(`App running on port ${PORT} (PID: ${process.pid})`);
});
EOF

Step 3: Start the Application with PM2

Start your application with PM2, assigning it a descriptive name:

cd ~/myapp
pm2 start app.js --name myapp

Pass environment variables and set the number of instances at start time:

# Start with a custom port via environment variable
pm2 start app.js --name myapp --env production -- PORT=3000

For applications that should use multiple CPU cores, enable cluster mode:

# Start in cluster mode with 4 workers
pm2 start app.js --name myapp -i 4

# Start with one worker per CPU core
pm2 start app.js --name myapp -i max

Step 4: Inspect the PM2 Process List

The pm2 list command displays a summary of all managed processes:

pm2 list

Example output:

┌────┬──────────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┐
│ id │ name         │ namespace   │ version │ mode    │ pid      │ uptime │ ↺    │ status    │
├────┼──────────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┤
│ 0  │ myapp        │ default     │ 1.0.0   │ cluster │ 12345    │ 2m     │ 0    │ online    │
└────┴──────────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┘

For detailed information about a specific process:

pm2 show myapp

Step 5: Manage Application Lifecycle

PM2 provides a comprehensive set of commands for managing application state:

# Restart an application (brief downtime)
pm2 restart myapp

# Reload without downtime (cluster mode only — recommended for production)
pm2 reload myapp

# Stop an application (keeps it in the PM2 list)
pm2 stop myapp

# Start a stopped application
pm2 start myapp

# Remove an application from PM2 management
pm2 delete myapp

# Restart all managed applications
pm2 restart all

# Stop all managed applications
pm2 stop all

Step 6: View Application Logs

PM2 collects stdout and stderr output for every managed process and stores them in ~/.pm2/logs/:

# Tail live logs for all processes
pm2 logs

# Tail logs for a specific application
pm2 logs myapp

# Show the last 100 lines of logs
pm2 logs myapp --lines 100

# Display only error logs
pm2 logs myapp --err

# Display only standard output logs
pm2 logs myapp --out

Log files are stored in:

~/.pm2/logs/myapp-out.log    # Standard output
~/.pm2/logs/myapp-error.log  # Standard error

Step 7: Configure systemd Startup Integration

By default, PM2 managed processes do not survive a server reboot. Use the pm2 startup command to generate and install a systemd unit that starts PM2 and all saved processes on boot:

pm2 startup systemd

PM2 outputs a command you must run with sudo to install the systemd unit. Copy and run the exact command from your output. It will look similar to:

sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd 
    -u youruser --hp /home/youruser

After running the sudo command, save the current process list so PM2 knows what to restore on reboot:

pm2 save

Verify the systemd service was created and enabled:

sudo systemctl status pm2-youruser
sudo systemctl is-enabled pm2-youruser

Test the full boot cycle by rebooting and checking that your application is running:

sudo reboot
# After reboot:
pm2 list

Step 8: Use an ecosystem.config.js for Multiple Applications

For production environments running multiple Node.js applications, an ecosystem configuration file is far cleaner than managing each app individually. Create ecosystem.config.js in your deployment directory:

cat > ~/ecosystem.config.js <<'EOF'
module.exports = {
  apps: [
    {
      name: 'api-server',
      script: '/var/www/api/app.js',
      instances: 'max',
      exec_mode: 'cluster',
      watch: false,
      max_memory_restart: '512M',
      env: {
        NODE_ENV: 'development',
        PORT: 3000
      },
      env_production: {
        NODE_ENV: 'production',
        PORT: 3000
      },
      error_file: '/var/log/pm2/api-server-error.log',
      out_file: '/var/log/pm2/api-server-out.log',
      log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
      merge_logs: true
    },
    {
      name: 'worker',
      script: '/var/www/worker/worker.js',
      instances: 2,
      exec_mode: 'cluster',
      watch: false,
      max_memory_restart: '256M',
      env_production: {
        NODE_ENV: 'production'
      },
      cron_restart: '0 3 * * *'
    }
  ]
};
EOF

Start all applications defined in the ecosystem file:

# Start all apps in development mode
pm2 start ecosystem.config.js

# Start all apps using the production environment block
pm2 start ecosystem.config.js --env production

# Reload all apps without downtime
pm2 reload ecosystem.config.js --env production

Key options in the ecosystem file:

  • instances: 'max' — spawn one process per CPU core
  • exec_mode: 'cluster' — enables Node.js cluster mode for load balancing across cores
  • max_memory_restart — automatically restart the process if it exceeds this memory limit
  • cron_restart — schedule automatic restarts using cron syntax
  • watch: true — restart the app when file changes are detected (use only in development)

Step 9: Monitor Applications with pm2 monit

PM2 includes a built-in terminal dashboard that shows real-time CPU and memory usage for all managed processes:

pm2 monit

The interface displays each process in a list on the left with CPU and memory graphs on the right. Use arrow keys to navigate between processes and view their logs inline. Press q to exit the monitor.

Step 10: Set Up Log Rotation with pm2-logrotate

Without log rotation, PM2 log files will grow indefinitely and eventually fill your disk. The pm2-logrotate module handles this automatically:

pm2 install pm2-logrotate

Configure rotation settings:

# Set maximum log file size before rotation (default: 10M)
pm2 set pm2-logrotate:max_size 50M

# Number of rotated files to keep (default: 30)
pm2 set pm2-logrotate:retain 7

# Enable compression of rotated files
pm2 set pm2-logrotate:compress true

# Set rotation schedule (default: every day at midnight)
pm2 set pm2-logrotate:rotateInterval '0 0 * * *'

# Verify settings
pm2 get pm2-logrotate

Log rotation runs automatically as a PM2 module in the background. No additional cron jobs are required.

Step 11: Updating the Saved Process List

Whenever you add, remove, or reconfigure applications and want those changes to persist across reboots, save the current process list:

pm2 save

If you need to clear all saved applications:

pm2 cleardump

Your Node.js applications are now managed by PM2 on RHEL 7 with full production-grade capabilities: automatic crash recovery, systemd boot integration, cluster mode for multi-core utilization, centralized logging, real-time monitoring, and automatic log rotation. The ecosystem configuration file approach makes managing multiple services maintainable and repeatable, functioning as declarative infrastructure-as-code for your Node.js deployment. As a natural next step, place Nginx in front of your PM2-managed application as a reverse proxy to handle SSL termination, serve static assets, and expose a standard port 80/443 endpoint to users.