Skip to content
Plurafy

Process Management

Process management controls how Node.js applications run, restart, and handle resources. Learn to use process managers for production deployment and monitoring.

Implement reliable process management that handles crashes, restarts, resource limits, and zero-downtime deployments.

Crash Handling: Automatic restart on failure Resource Management: Monitor and limit CPU, memory Zero-Downtime Deployment: Graceful restarts Clustering: Multi-core utilization Logging: Centralized process output

  • PM2: Most popular, feature-rich
  • Forever: Simple, lightweight
  • Systemd: OS-level management
  • Docker: Container-based management
  • Kubernetes: Orchestration platform
ecosystem.config.js
module.exports = {
apps: [
{
name: 'app',
script: './server.js',
instances: 'max',
exec_mode: 'cluster',
// Environment
env: {
NODE_ENV: 'development',
},
env_production: {
NODE_ENV: 'production',
},
// Resource limits
max_memory_restart: '500M',
// Restart configuration
watch: ['src'],
watch_delay: 1000,
ignore_watch: ['node_modules', 'logs'],
// Graceful shutdown
kill_timeout: 5000,
listen_timeout: 10000,
// Error handling
error_file: './logs/error.log',
out_file: './logs/out.log',
log_file: './logs/combined.log',
// Merge logs
merge_logs: true,
// Restart policy
max_restarts: 10,
min_uptime: '10s',
// Autorestart on file changes
watch: true,
ext_type: 'js,json',
},
],
};
const cluster = require('cluster');
const os = require('os');
const express = require('express');
const numCores = os.cpus().length;
if (cluster.isMaster) {
console.log(`Master process ${process.pid} is running`);
// Fork workers
for (let i = 0; i < numCores; i++) {
cluster.fork();
}
// Handle worker death
cluster.on('exit', (worker, code, signal) => {
console.log(`Worker ${worker.process.pid} died`);
// Restart worker
cluster.fork();
});
// Graceful shutdown
process.on('SIGTERM', () => {
console.log('SIGTERM received, shutting down gracefully');
const workers = Object.values(cluster.workers);
workers.forEach(worker => worker.kill());
});
} else {
const app = express();
app.get('/', (req, res) => {
res.json({
message: 'Hello',
pid: process.pid,
});
});
app.listen(3000, () => {
console.log(`Worker ${process.pid} started`);
});
}
class GracefulShutdown {
constructor(server, options = {}) {
this.server = server;
this.timeout = options.timeout || 30000;
this.callbacks = [];
}
onShutdown(callback) {
this.callbacks.push(callback);
}
async shutdown(signal) {
console.log(`${signal} received, starting graceful shutdown`);
// Stop accepting new requests
this.server.close(() => {
console.log('HTTP server closed');
});
// Close existing connections
const activeConnections = new Set();
this.server.on('connection', (conn) => {
activeConnections.add(conn);
conn.on('close', () => activeConnections.delete(conn));
});
// Wait for active connections
const shutdownTimeout = setTimeout(() => {
console.error('Shutdown timeout exceeded');
activeConnections.forEach(conn => conn.destroy());
}, this.timeout);
// Run callbacks
for (const callback of this.callbacks) {
try {
await callback();
} catch (error) {
console.error('Shutdown callback error:', error);
}
}
clearTimeout(shutdownTimeout);
process.exit(0);
}
register() {
process.on('SIGTERM', () => this.shutdown('SIGTERM'));
process.on('SIGINT', () => this.shutdown('SIGINT'));
}
}
// Usage
const express = require('express');
const app = express();
const server = app.listen(3000);
const shutdown = new GracefulShutdown(server, { timeout: 30000 });
// Register cleanup callbacks
shutdown.onShutdown(async () => {
console.log('Closing database connection');
await database.close();
});
shutdown.onShutdown(async () => {
console.log('Closing cache connection');
await cache.disconnect();
});
shutdown.register();
class ProcessMonitor {
constructor(options = {}) {
this.checkInterval = options.checkInterval || 5000;
this.memoryThreshold = options.memoryThreshold || 500 * 1024 * 1024; // 500MB
this.cpuThreshold = options.cpuThreshold || 80; // percent
}
start() {
setInterval(() => {
this.checkHealth();
}, this.checkInterval);
}
checkHealth() {
const memUsage = process.memoryUsage();
const heapPercent = (memUsage.heapUsed / memUsage.heapTotal) * 100;
// Memory check
if (memUsage.heapUsed > this.memoryThreshold) {
console.warn('High memory usage:', {
heapUsed: Math.round(memUsage.heapUsed / 1024 / 1024) + 'MB',
heapTotal: Math.round(memUsage.heapTotal / 1024 / 1024) + 'MB',
heapPercent: heapPercent.toFixed(2) + '%',
});
// Trigger garbage collection
if (global.gc) {
global.gc();
}
// Restart if critical
if (heapPercent > 95) {
this.gracefulRestart();
}
}
// CPU check
const usage = process.cpuUsage();
const totalCPU = (usage.user + usage.system) / 1000000; // seconds
console.log('CPU usage:', totalCPU.toFixed(2) + 's');
}
gracefulRestart() {
console.log('Initiating graceful restart due to resource limits');
process.exit(0); // Let process manager restart
}
}
const monitor = new ProcessMonitor({
memoryThreshold: 300 * 1024 * 1024,
checkInterval: 10000,
});
monitor.start();
const cluster = require('cluster');
if (cluster.isMaster) {
const workers = [];
// Create workers
for (let i = 0; i < 2; i++) {
const worker = cluster.fork();
workers.push(worker);
}
// Send message to all workers
function broadcastMessage(message) {
workers.forEach(worker => {
worker.send(message);
});
}
// Handle worker messages
Object.values(cluster.workers).forEach(worker => {
worker.on('message', (msg) => {
console.log('Master received:', msg);
if (msg.type === 'task-complete') {
// Broadcast completion to other workers
broadcastMessage({
type: 'task-result',
data: msg.data,
});
}
});
});
} else {
// Worker process
process.on('message', (msg) => {
console.log('Worker received:', msg);
if (msg.type === 'start-task') {
// Do work
process.send({
type: 'task-complete',
data: 'Task finished',
});
}
});
console.log(`Worker ${process.pid} started`);
}
const express = require('express');
const http = require('http');
class ZeroDowntimeServer {
constructor(app, port = 3000) {
this.app = app;
this.port = port;
this.server = null;
this.activeConnections = new Set();
this.isShuttingDown = false;
}
start() {
this.server = http.createServer(this.app);
this.server.on('connection', (connection) => {
this.activeConnections.add(connection);
connection.on('close', () => {
this.activeConnections.delete(connection);
});
// Close connection after timeout during shutdown
if (this.isShuttingDown) {
connection.setTimeout(5000, () => connection.destroy());
}
});
this.registerShutdownHandlers();
return new Promise((resolve) => {
this.server.listen(this.port, () => {
console.log(`Server listening on port ${this.port}`);
resolve();
});
});
}
registerShutdownHandlers() {
const signals = ['SIGTERM', 'SIGINT'];
signals.forEach(signal => {
process.on(signal, async () => {
console.log(`Received ${signal}, starting graceful shutdown`);
await this.shutdown();
});
});
}
async shutdown() {
if (this.isShuttingDown) return;
this.isShuttingDown = true;
// Stop accepting new requests
this.server.close();
// Wait for active connections
const shutdownTimeout = new Promise(resolve =>
setTimeout(resolve, 30000)
);
await Promise.race([
new Promise(resolve => {
const check = setInterval(() => {
if (this.activeConnections.size === 0) {
clearInterval(check);
resolve();
}
}, 1000);
}),
shutdownTimeout,
]);
// Force close remaining connections
this.activeConnections.forEach(connection => connection.destroy());
process.exit(0);
}
}
// Usage
const app = express();
const server = new ZeroDowntimeServer(app, 3000);
server.start();
  1. Configuration

    • Use process manager configuration files
    • Separate dev and production configs
    • Version control config files
    • Document all options
  2. Monitoring

    • Monitor memory and CPU usage
    • Set resource limits
    • Alert on process restarts
    • Log all process events
  3. Graceful Shutdown

    • Handle signals properly
    • Close resources cleanly
    • Set shutdown timeouts
    • Drain connection queues
  4. High Availability

    • Run multiple instances
    • Load balance traffic
    • Health check endpoints
    • Auto-restart on failure
  5. Deployment

    • Zero-downtime deployments
    • Rolling restarts
    • Backward compatibility
    • Quick rollback capability
  • Production Servers: PM2 for clustering
  • Cloud Deployments: Docker with orchestration
  • CI/CD Pipelines: Automated restarts
  • Microservices: Independent process management
  • Load Balancing: Multi-process distribution