PM2 Complete Guide | Production Process Manager for Node.js
이 글의 핵심
PM2 is a production process manager for Node.js. It provides clustering, monitoring, logging, zero-downtime reloads, and automatic restarts for reliable Node.js deployments.
Introduction
PM2 is a production-grade process manager for Node.js applications. It keeps your app running 24/7, handles crashes, enables clustering, and provides monitoring and logging.
Why PM2?
Without PM2:
node app.js
# App crashes → stays down
# Server restarts → app doesn't start
# 1 process → can't use all CPU cores
With PM2:
pm2 start app.js
# App crashes → auto-restart
# Server restarts → app auto-starts
# Cluster mode → uses all CPU cores
# Built-in monitoring and logs
1. Installation
# Global install (recommended)
npm install -g pm2
# Or with yarn
yarn global add pm2
# Verify
pm2 --version
2. Basic Usage
# Start app
pm2 start app.js
# Start with name
pm2 start app.js --name my-app
# Start with watch (auto-reload on file change)
pm2 start app.js --watch
# Start with environment
pm2 start app.js --env production
# List all processes
pm2 list
# Stop app
pm2 stop my-app
# Restart app
pm2 restart my-app
# Delete app from PM2
pm2 delete my-app
# Stop all
pm2 stop all
# Restart all
pm2 restart all
# Delete all
pm2 delete all
3. Cluster Mode
# Start in cluster mode (uses all CPU cores)
pm2 start app.js -i max
# Start with specific number of instances
pm2 start app.js -i 4
# Scale up/down
pm2 scale my-app 8 # Scale to 8 instances
pm2 scale my-app +2 # Add 2 more instances
pm2 scale my-app -2 # Remove 2 instances
Example: Cluster-Ready Express App
// app.js
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send(`Hello from process ${process.pid}`);
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}, PID: ${process.pid}`);
});
# Start 4 instances
pm2 start app.js -i 4
# Each request handled by different process
curl http://localhost:3000 # Process 1234
curl http://localhost:3000 # Process 1235
curl http://localhost:3000 # Process 1236
curl http://localhost:3000 # Process 1237
4. Ecosystem File
# Generate ecosystem file
pm2 ecosystem
// ecosystem.config.js
module.exports = {
apps: [
{
name: 'api',
script: './app.js',
instances: 'max',
exec_mode: 'cluster',
env: {
NODE_ENV: 'development',
PORT: 3000,
},
env_production: {
NODE_ENV: 'production',
PORT: 8080,
},
error_file: './logs/err.log',
out_file: './logs/out.log',
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
max_memory_restart: '500M',
watch: false,
ignore_watch: ['node_modules', 'logs'],
},
{
name: 'worker',
script: './worker.js',
instances: 2,
exec_mode: 'cluster',
cron_restart: '0 0 * * *', // Restart at midnight
},
],
};
# Start from ecosystem file
pm2 start ecosystem.config.js
# Start with specific environment
pm2 start ecosystem.config.js --env production
# Restart all apps in ecosystem
pm2 restart ecosystem.config.js
# Stop all apps in ecosystem
pm2 stop ecosystem.config.js
5. Monitoring
# Real-time monitoring
pm2 monit
# Show detailed info
pm2 show my-app
# Show logs
pm2 logs
# Show logs for specific app
pm2 logs my-app
# Show only errors
pm2 logs --err
# Flush all logs
pm2 flush
# Dashboard (web UI)
pm2 plus
6. Logging
# View logs
pm2 logs
# View last 100 lines
pm2 logs --lines 100
# Real-time logs
pm2 logs --raw
# JSON logs
pm2 logs --json
# Log file locations
~/.pm2/logs/app-out.log # stdout
~/.pm2/logs/app-error.log # stderr
Custom Log Files
// ecosystem.config.js
module.exports = {
apps: [{
name: 'api',
script: './app.js',
error_file: './logs/api-error.log',
out_file: './logs/api-out.log',
merge_logs: true,
log_date_format: 'YYYY-MM-DD HH:mm:ss',
}],
};
Log Rotation
# Install PM2 log rotate
pm2 install pm2-logrotate
# Configure rotation
pm2 set pm2-logrotate:max_size 10M # Max size per file
pm2 set pm2-logrotate:retain 7 # Keep 7 files
pm2 set pm2-logrotate:compress true # Compress old logs
pm2 set pm2-logrotate:rotateInterval '0 0 * * *' # Rotate daily
7. Auto-Restart on Server Reboot
# Save current PM2 process list
pm2 save
# Generate startup script
pm2 startup
# Follow instructions to run the generated command
# Example: sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u user --hp /home/user
# Now PM2 will auto-start on server reboot
# To remove startup script
pm2 unstartup
8. Zero-Downtime Reload
# Reload app (zero downtime, cluster mode only)
pm2 reload my-app
# Graceful reload (waits for existing requests)
pm2 reload my-app --wait-ready
# Restart (downtime)
pm2 restart my-app
Graceful Shutdown
// app.js
const express = require('express');
const app = express();
const server = app.listen(3000);
// Listen for shutdown signal
process.on('SIGINT', () => {
console.log('Received SIGINT, graceful shutdown...');
server.close(() => {
console.log('Closed all connections');
process.exit(0);
});
// Force shutdown after 30s
setTimeout(() => {
console.error('Forced shutdown');
process.exit(1);
}, 30000);
});
9. Environment Variables
# Set environment variables
pm2 start app.js --env production
# Or via ecosystem file
pm2 start ecosystem.config.js --env production
// ecosystem.config.js
module.exports = {
apps: [{
name: 'api',
script: './app.js',
env: {
NODE_ENV: 'development',
PORT: 3000,
DB_HOST: 'localhost',
},
env_production: {
NODE_ENV: 'production',
PORT: 8080,
DB_HOST: 'prod-db.example.com',
},
env_staging: {
NODE_ENV: 'staging',
PORT: 8080,
DB_HOST: 'staging-db.example.com',
},
}],
};
10. TypeScript Support
# Install ts-node
npm install -D ts-node typescript
# Start TypeScript app
pm2 start app.ts --interpreter ts-node
# Or compile first (recommended for production)
npm run build
pm2 start dist/app.js
// ecosystem.config.js for TypeScript
module.exports = {
apps: [{
name: 'api',
script: './src/app.ts',
interpreter: 'ts-node',
watch: ['src'],
ignore_watch: ['node_modules', 'dist'],
env: {
TS_NODE_PROJECT: './tsconfig.json',
},
}],
};
11. Memory Management
// ecosystem.config.js
module.exports = {
apps: [{
name: 'api',
script: './app.js',
max_memory_restart: '500M', // Restart if memory exceeds 500MB
max_restarts: 10, // Max restarts within restart_delay
min_uptime: '10s', // Min uptime before considered stable
restart_delay: 4000, // Delay between restarts (ms)
}],
};
12. Monitoring with PM2 Plus
# Link to PM2 Plus (free tier available)
pm2 plus
# Or with specific bucket
pm2 link <secret_key> <public_key>
# Disconnect
pm2 unlink
PM2 Plus provides:
- Real-time dashboard
- Error tracking
- Transaction tracing
- Custom metrics
- Email/Slack alerts
13. Docker Integration
# Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm install -g pm2
EXPOSE 3000
CMD ["pm2-runtime", "ecosystem.config.js"]
# docker-compose.yml
version: '3.8'
services:
api:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
restart: unless-stopped
14. Best Practices
1. Use Ecosystem File
# Good: version-controlled config
pm2 start ecosystem.config.js
# Bad: CLI flags (hard to reproduce)
pm2 start app.js -i 4 --max-memory-restart 500M --env production
2. Cluster Mode for Web Servers
module.exports = {
apps: [{
name: 'web-server',
script: './app.js',
instances: 'max', // Use all CPUs
exec_mode: 'cluster', // Enable clustering
}],
};
3. Fork Mode for Workers
module.exports = {
apps: [{
name: 'queue-worker',
script: './worker.js',
instances: 1, // Single instance
exec_mode: 'fork', // Fork mode
}],
};
4. Log Rotation
pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 7
5. Startup Script
pm2 save
pm2 startup
15. Common Commands
# Status
pm2 status # List all processes
pm2 list # Same as status
pm2 show <app> # Detailed info
# Logs
pm2 logs # All logs
pm2 logs <app> # App logs
pm2 logs --lines 200 # Last 200 lines
pm2 flush # Clear logs
# Control
pm2 start <app>
pm2 stop <app>
pm2 restart <app>
pm2 reload <app> # Zero-downtime (cluster only)
pm2 delete <app>
# Monitoring
pm2 monit # Real-time monitor
pm2 plus # Web dashboard
# Maintenance
pm2 save # Save process list
pm2 startup # Auto-start on boot
pm2 resurrect # Restore saved processes
pm2 update # Update PM2 in-memory
pm2 reset <app> # Reset restart counter
16. Real-World Example
// ecosystem.config.js
module.exports = {
apps: [
// API server (cluster mode)
{
name: 'api',
script: './dist/api/server.js',
instances: 'max',
exec_mode: 'cluster',
env_production: {
NODE_ENV: 'production',
PORT: 8080,
DB_HOST: process.env.DB_HOST,
REDIS_URL: process.env.REDIS_URL,
},
error_file: './logs/api-error.log',
out_file: './logs/api-out.log',
max_memory_restart: '1G',
min_uptime: '10s',
max_restarts: 10,
},
// Background worker (fork mode)
{
name: 'worker',
script: './dist/worker/index.js',
instances: 2,
exec_mode: 'fork',
env_production: {
NODE_ENV: 'production',
REDIS_URL: process.env.REDIS_URL,
},
error_file: './logs/worker-error.log',
out_file: './logs/worker-out.log',
max_memory_restart: '500M',
cron_restart: '0 3 * * *', // Restart at 3 AM daily
},
// Cron job
{
name: 'cleanup',
script: './dist/jobs/cleanup.js',
instances: 1,
cron_restart: '0 0 * * *', // Run at midnight
autorestart: false,
},
],
};
# Deploy
npm run build
pm2 start ecosystem.config.js --env production
pm2 save
pm2 startup
# Update
git pull
npm run build
pm2 reload ecosystem.config.js --env production
Summary
PM2 is essential for Node.js production:
- Process management with auto-restart
- Cluster mode for multi-core scaling
- Zero-downtime reloads
- Monitoring and logging
- Startup scripts for auto-start
Key Takeaways:
- Always use PM2 in production
- Enable cluster mode for web servers
- Configure log rotation
- Set up startup script
- Use ecosystem file for config
Next Steps:
- Deploy with Docker
- Reverse proxy with Nginx
- Monitor with Winston
Resources: