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 coreexec_mode: 'cluster'— enables Node.js cluster mode for load balancing across coresmax_memory_restart— automatically restart the process if it exceeds this memory limitcron_restart— schedule automatic restarts using cron syntaxwatch: 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.