Learn how to create, monitor, and manage multiple processes for better performance, scalability, and reliability in your Node.js applications.
Node.js runs JavaScript in a single thread with an event-driven, non-blocking I/O model. While this is efficient for I/O-bound tasks, CPU-intensive operations can block the entire application. Process management helps overcome this limitation.
Maximize performance across all available processors
Prevent crashes from affecting the entire system
Update without interrupting service
// Single Thread vs Multi-Process Architecture
// Traditional Node.js (Single Thread)
console.log('Main thread running');
heavyComputation(); // Blocks everything!
console.log('Waiting...');
// Multi-Process Architecture
const cluster = require('cluster');
if (cluster.isMaster) {
// Fork workers for each CPU core
const numCPUs = require('os').cpus().length;
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
} else {
// Each worker runs independently
console.log(`Worker ${process.pid} started`);
}
Understanding the building blocks of process management in Node.js
Global object providing information and control over the current process.
console.log(process.pid); // Process ID
console.log(process.version); // Node version
console.log(process.platform); // Operating system
console.log(process.uptime()); // Uptime in seconds
console.log(process.memoryUsage()); // Memory stats
Spawn new processes and execute system commands.
spawn()
Launches new process with streams
exec()
Executes command in shell
fork()
Creates new Node.js process
const { spawn } = require('child_process');
const ls = spawn('ls', ['-lh', '/usr']);
ls.stdout.on('data', (data) => {
console.log(`Output: ${data}`);
});
ls.stderr.on('data', (data) => {
console.error(`Error: ${data}`);
});
ls.on('close', (code) => {
console.log(`Process exited with code ${code}`);
});
Choose the right module for your specific use case
Multiple processes sharing server ports for web applications
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker) => {
console.log(`Worker ${worker.process.pid} died`);
cluster.fork(); // Replace dead worker
});
} else {
http.createServer((req, res) => {
res.writeHead(200);
res.end('Hello World\n');
}).listen(8000);
}
Spawn external commands and Node.js scripts
Best for running system commands or isolating untrusted code.
CPU-intensive tasks with parallel execution
const { Worker } = require('worker_threads');
const worker = new Worker('./worker.js', {
workerData: { value: 5 } // Data to pass
});
worker.on('message', (result) => {
console.log('Result:', result);
});
worker.on('error', (error) => {
console.error(error);
});
Monitor and control the current process
Production-ready tools for managing Node.js processes
The most popular production process manager for Node.js with built-in load balancing, monitoring, and automatic restarts.
# Install PM2 globally
npm install -g pm2
# Start application with max instances
pm2 start app.js -i max
# Common commands
pm2 list # List all processes
pm2 restart app # Restart application
pm2 stop app # Stop application
pm2 logs # View logs
pm2 monit # Monitor resources
Simple alternative to PM2 for basic process management needs.
forever start app.js
forever list
forever stop app.js
Development tool that auto-restarts on file changes.
npm install -g nodemon
nodemon app.js
| Scenario | Recommended Tool | Reason |
|---|---|---|
| Simple applications, low traffic | Single Process | No need for complexity |
| Web servers with multiple CPUs | Cluster Mode | Maximize CPU utilization |
| Running external commands | Child Processes | Execute system scripts |
| CPU-intensive computations | Worker Threads | Parallel JavaScript execution |
| Production deployment | PM2/Forever | Reliability and monitoring |
Essential patterns for robust production applications
Properly close connections and cleanup resources before exiting to prevent data loss.
process.on('SIGTERM', () => {
console.log('SIGTERM received, closing server...');
server.close(() => {
console.log('Server closed');
// Close database connections
mongoose.connection.close(false, () => {
console.log('Database connections closed');
process.exit(0);
});
});
});
Catch uncaught exceptions and unhandled rejections to prevent crashes.
process.on('uncaughtException', (error) => {
console.error('Uncaught Exception:', error);
// Log to monitoring service
logger.fatal(error);
process.exit(1);
});
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
// Application specific logging
logger.error('Unhandled Rejection', { reason, promise });
});
Enable processes to communicate with each other using messages.
const child = fork('child.js');
child.send({ cmd: 'start', data: [1,2,3] });
child.on('message', (msg) => {
console.log('Received:', msg);
});
process.on('message', (msg) => {
console.log('Received:', msg);
process.send({ result: 'processed' });
});
Maximize CPU utilization by running multiple worker processes behind a load balancer.
Monitor process status and automatically restart unhealthy processes before they fail.
Use PM2 or Forever to restart crashed processes automatically and maintain uptime.
Keep detailed logs for debugging, monitoring, and auditing process behavior.
Implement graceful shutdown for SIGTERM and SIGINT signals in containerized environments.
Prevent memory leaks from crashing the system by setting --max-old-space-size limits.
Process management is crucial for building robust, scalable Node.js applications that can handle production workloads efficiently and recover from failures automatically. Choose the right tool and pattern based on your specific needs.