How I Use PM2 to Keep 9 Node.js Services Running
The Problem with Long-Running Services
On my production server, I run nine services: API servers, pipeline workers, file watchers, and scheduled tasks. Without a process manager, any crashed service stays dead until I notice and manually restart it. That is not acceptable for production workloads.
PM2 is a process manager for Node.js (and any other language) that handles automatic restarts, log management, clustering, and monitoring. I have been using it for over a year and it has been rock-solid.
My Ecosystem Configuration
PM2 uses an ecosystem file to define all managed processes. Here is a simplified version of mine:
// ecosystem.config.js
module.exports = {
apps: [
{
name: 'api-server',
script: './api/dist/index.js',
instances: 2,
exec_mode: 'cluster',
max_memory_restart: '512M',
env: {
NODE_ENV: 'production',
PORT: 3000
}
},
{
name: 'video-pipeline',
script: '/home/user/video-pipeline/venv/bin/python',
args: '/home/user/video-pipeline/src/main.py',
cwd: '/home/user/video-pipeline',
max_memory_restart: '1G',
cron_restart: '0 4 * * *'
},
{
name: 'drive-watcher',
script: '/home/user/drive-watcher/venv/bin/python',
args: '/home/user/drive-watcher/watcher.py',
cwd: '/home/user/drive-watcher',
max_memory_restart: '256M',
autorestart: true,
restart_delay: 5000
},
{
name: 'blog-api',
script: './blog-api/index.js',
max_memory_restart: '256M'
},
{
name: 'webhook-handler',
script: './webhooks/index.js',
max_memory_restart: '256M',
instances: 1
}
]
}Let me break down the key configuration options I use.
Cluster Mode
For the API server, I run two instances in cluster mode. PM2 load-balances requests across them, giving me both redundancy and better performance on multi-core machines:
instances: 2,
exec_mode: 'cluster'If one instance crashes, the other keeps serving while PM2 restarts the failed one. Users experience zero downtime.
Memory Limits
The max_memory_restart setting automatically restarts a process if it exceeds the memory threshold. This catches memory leaks before they crash the server:
max_memory_restart: '512M'I set different limits per service based on their expected memory usage. The video pipeline needs more memory for FFmpeg operations, while the webhook handler should never exceed 256 MB.
Python Services
PM2 manages Python services by pointing to the virtual environment's Python binary directly. No activation scripts needed:
script: '/home/user/video-pipeline/venv/bin/python',
args: '/home/user/video-pipeline/src/main.py',
cwd: '/home/user/video-pipeline'Setting the cwd (current working directory) is important so relative file paths in the Python code resolve correctly.
Essential PM2 Commands
# Start all services from ecosystem file
pm2 start ecosystem.config.js
# List all running processes
pm2 list
# View logs for a specific service
pm2 logs video-pipeline --lines 100
# Restart a service
pm2 restart api-server
# Stop a service
pm2 stop drive-watcher
# Monitor all services in real-time
pm2 monit
# Save current process list for auto-start on reboot
pm2 save
# Setup startup script (survives server reboots)
pm2 startupThe pm2 startup command is critical. It generates a system service that starts PM2 and all your saved processes automatically when the server boots. Without this, a server restart would leave all services down.
Log Management
PM2 captures stdout and stderr for every process. By default, logs grow indefinitely, so I use pm2-logrotate:
# Install the log rotation module
pm2 install pm2-logrotate
# Configure rotation settings
pm2 set pm2-logrotate:max_size 50M
pm2 set pm2-logrotate:retain 7
pm2 set pm2-logrotate:compress trueThis keeps the last 7 rotated log files, each up to 50 MB, compressed. Without rotation, I once had a verbose service fill up 20 GB of disk space with logs.
Monitoring
The pm2 monit command opens an interactive dashboard showing CPU usage, memory consumption, and logs for all processes. For remote monitoring, I combine PM2's status data with my Telegram notification system:
# Check if any process is errored or stopped
pm2 jlist | python3 -c "
import json, sys
procs = json.loads(sys.stdin.read())
for p in procs:
if p['pm2_env']['status'] != 'online':
print(f\"ALERT: {p['name']} is {p['pm2_env']['status']}\")
"This script runs via cron every 5 minutes and sends a Telegram alert if any service is not online.
Graceful Reloads
For Node.js services in cluster mode, PM2 supports zero-downtime reloads:
pm2 reload api-serverThis restarts instances one at a time, waiting for each to be ready before restarting the next. Users never experience downtime during deployments.
My PM2 Checklist for New Services
- Add to ecosystem.config.js with appropriate memory limits
- Set cwd for Python services
- Configure restart delays for services that depend on external APIs
- Run
pm2 saveafter adding new services - Verify the service survives a
pm2 restartcleanly
PM2 is one of those tools that fades into the background once configured. It just works, keeping your services running 24/7 without intervention.