Production-Grade Node.js Process Management with PM2
Introduction to PM2
PM2 serves as a production-level process manager tailored for Node.js applications. It provides essential features such as load balancing, automatic process resurrection, centralized log administration, and real-time system metrics. By operating applications as daemon processes, PM2 guarantees service continuity even during unexpected crashes or system reboots. The tool also facilitates zero-downtime deployments and integrates seamlessly with containerized environments like Docker.
Core Capabilities
PM2 addresses several critical operational challenges in production environments:
- Process Resilience: Automatically revives crashed or terminated processes to maintain service availability.
- Horizontal Scaling: Utilizes cluster mode to spawn multiple instances, distributing traffic across CPU cores.
- Seamless Updates: Supports reloading applications without interrupting active connections.
- Log Aggregation: Consolidates logs from multiple instances for easier debugging.
- Resource Monitoring: Tracks memory and CPU consumption in real-time.
Installation
Install the package globally to make the CLI available system-wide:
npm install pm2 -gVerify the installation:
pm2 --versionEssential CLI Commands
| Command | Description | Examples |
|---|---|---|
start | Initialize an application | pm2 start server.js --name api-server |
stop | Terminate a running process | pm2 stop api-serverpm2 stop all |
restart | Reboot an application | pm2 restart api-server |
delete | Remove a process from PM2 | pm2 delete api-server |
list | Display all managed processes | pm2 list |
monit | Open the monitoring dashboard | pm2 monit |
logs | Stream application logs | pm2 logs --lines 200 |
save | Persist the current process list | pm2 save |
reload | Perform zero-downtime reload | pm2 reload all |
scale | Adjust instance count | pm2 scale api-server +2 |
startup | Configure boot auto-start | pm2 startup |
Declarative Configuration with Ecosystem Files
The ecosystem.config.js file allows for repeatable and version-controlled deployments. It supports defining multiple applications and environment-specific variables within a single source.
Basic Configuration Structure
module.exports = {
apps: [
{
name: 'web-service',
script: './dist/index.js',
instances: 'max',
exec_mode: 'cluster',
autorestart: true,
watch: false,
max_memory_restart: '2G',
env: {
NODE_ENV: 'development',
PORT: 3000
},
env_production: {
NODE_ENV: 'production',
PORT: 8080
}
}
]
};Key Configuration Fields
- apps: Array of application configuration objects.
- name: Unique identifier for the process.
- script: Entry point file path.
- instances: Number of clusters; use
'max'to match CPU cores. - exec_mode: Set to
'cluster'for load balancing. - max_memory_restart: Threshold to trigger automatic restart.
- env: Default environment variables.
- env_production: Variables specific to the production environment.
Launch applications using the configuration file:
pm2 start ecosystem.config.js --env productionCommand-Line Configuration Alternatives
For ad-hoc operations, CLI flags can replicate configuration file functionality:
# Start with a specific name and instances
pm2 start server.js --name web-api -i 4
# Enable file watching for development
pm2 start server.js --watch
# Set memory limit for auto-restart
pm2 start server.js --max-memory-restart 1G
# Pass environment variables
pm2 start server.js --node-args="--max-old-space-size=2048"Real-Time Monitoring
PM2 includes a terminal-based monitoring interface:
pm2 monitThis command launches an interactive dashboard displaying real-time CPU usage, memory consumption, and logs for all processes. For distributed monitoring and historical data analysis, PM2 Plus offers a web-based dashboard with alerting capabilities.
Session Management in Cluster Mode
When operating multiple instances, in-memory session storage becomes problematic because requests from the same client may be routed to different instances. External session stores resolve this issue.
Using Redis with Express for session sharing:
const express = require('express');
const session = require('express-session');
const RedisStore = require('connect-redis').default;
const app = express();
app.use(
session({
store: new RedisStore({ url: 'redis://localhost:6379' }),
secret: 'secure-session-key',
resave: false,
saveUninitialized: false,
cookie: { secure: false }
})
);Graceful Shutdown Implementation
To ensure application state is preserved during restarts, handle termination signals properly:
const express = require('express');
const app = express();
let isShuttingDown = false;
const activeRequests = new Set();
app.use((req, res, next) => {
if (isShuttingDown) {
return res.status(503).send('Server is shutting down');
}
activeRequests.add(res);
res.on('finish', () => activeRequests.delete(res));
next();
});
const server = app.listen(3000);
const shutdownHandler = () => {
isShuttingDown = true;
console.log('Initiating graceful shutdown...');
server.close(() => {
console.log('HTTP server closed');
});
const checkInterval = setInterval(() => {
if (activeRequests.size === 0) {
clearInterval(checkInterval);
console.log('All requests completed. Exiting.');
process.exit(0);
}
console.log(`Waiting for ${activeRequests.size} requests to finish...`);
}, 1000);
};
process.on('SIGTERM', shutdownHandler);
process.on('SIGINT', shutdownHandler);Shutdown Timeout Configuration
PM2 waits a default period before forcing a process kill. Adjust this timeout if the cleanup logic requires more time:
pm2 start server.js --kill-timeout 5000This configuration grants the application 5000 milliseconds to complete cleanup before PM2 issues a SIGKILL signal. The timeout ensures that PM2 retains control over misbehaving processes while allowing properly designed applications to shut down cleanly.