Node.js is single-threaded; a lone process uses a single CPU core. On an 8-core box you leave 87% of the hardware idle. Cluster mode plus PM2 gives you every core and zero-downtime deploys. This guide walks through every step of a production setup.
What Is Cluster Mode?
Related guides: PostgreSQL optimization · Advanced Git commands · What is Redis · Deploying with Docker · Docker Compose guide
Node.js's cluster module spawns multiple workers under a single master process. Workers share the same port, and the kernel distributes incoming connections between them. 8 cores means 8 workers means roughly 8x throughput.
Installing PM2 and Basic Usage
# Global install
npm i -g pm2
# Start a cluster with one command
pm2 start server.js -i max --name myapp
# -i max = one worker per core
# Status
pm2 list
pm2 monit # live dashboard
pm2 logs myapp --lines 100
pm2 describe myapp
# Operations
pm2 restart myapp
pm2 reload myapp # zero-downtime — rolling worker restart
pm2 stop myapp
pm2 delete myapp
ecosystem.config.js
In production, use a config file instead of inline flags. Every setting lives in version control.
// ecosystem.config.js
module.exports = {
apps: [{
name: 'keydal',
script: './server.js',
instances: 'max', // every core
exec_mode: 'cluster',
// Restart if memory exceeds this
max_memory_restart: '512M',
// Env
env: {
NODE_ENV: 'development',
PORT: 3000
},
env_production: {
NODE_ENV: 'production',
PORT: 3000
},
// Logs
out_file: '/var/log/keydal/out.log',
error_file: '/var/log/keydal/error.log',
log_date_format: 'YYYY-MM-DD HH:mm:ss',
merge_logs: true,
// Restart behaviour
autorestart: true,
watch: false,
max_restarts: 10,
min_uptime: '10s',
// Graceful shutdown
kill_timeout: 5000,
listen_timeout: 3000,
wait_ready: true
}]
};
# Start in production
pm2 start ecosystem.config.js --env production
# Auto-start on boot
pm2 startup
pm2 save
Zero-Downtime Deploys
pm2 reload rolls workers one by one. A new worker comes up, the old one is killed — users notice nothing. That is different from restart, which kills every worker simultaneously.
// server.js — graceful shutdown
const server = app.listen(PORT, () => {
console.log(`Server started on ${PORT}`);
if (process.send) process.send('ready'); // tell PM2 we are ready
});
process.on('SIGINT', gracefulShutdown);
process.on('SIGTERM', gracefulShutdown);
function gracefulShutdown() {
console.log('Shutting down gracefully...');
server.close(() => {
console.log('HTTP server closed');
db.pool.end(() => process.exit(0));
});
// Force exit after 10 seconds
setTimeout(() => process.exit(1), 10000);
}
wait_ready: true + process.send('ready') combination tells PM2 a worker is fully up. The old worker is not killed until that signal arrives.Log Management
# Add log rotation (otherwise log files grow unbounded)
pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 50M
pm2 set pm2-logrotate:retain 14
pm2 set pm2-logrotate:compress true
pm2 set pm2-logrotate:rotateInterval '0 0 * * *'
Monitoring
pm2 monit— local dashboard- PM2 Plus — cloud dashboard with a free tier
- Prometheus export via
pm2-prom-module - OpenTelemetry for Datadog or New Relic
Finding Memory Leaks
# Take a heap snapshot
pm2 restart myapp --node-args='--inspect'
# Chrome DevTools, chrome://inspect, Memory tab
# Or use clinic
npm i -g clinic
clinic doctor -- node server.js
# Send traffic for 30 seconds, then Ctrl+C
# An HTML report opens in the browser
Cluster vs Docker/Kubernetes
PM2 cluster mode is usually unnecessary in Kubernetes — K8s runs each pod as a single process and scales horizontally through the pod autoscaler. PM2 cluster makes sense on a single VPS or dedicated server.
fork mode with a single worker and let K8s handle scaling. Cluster mode doubles memory usage in-pod.Deploy Workflow
# Typical deploy script
#!/bin/bash
set -e
BRANCH=${1:-main}
cd /var/www/myapp
git fetch origin
git checkout $BRANCH
git pull origin $BRANCH
npm ci --production
npm run build # if any
pm2 reload ecosystem.config.js --env production
pm2 save
echo "Deploy done."
Modern Software Development and DevOps Practices
Professional software development rests on three pillars: version control (Git + GitHub/GitLab pull request flow, mandatory code review), CI/CD pipeline (automated test + lint + build + deploy), and observability (Sentry/Datadog/Grafana for logs, metrics, traces). The test pyramid (unit > integration > e2e) ensures code quality, microservice architecture uses Docker containers with Kubernetes orchestration, and REST or GraphQL APIs follow OpenAPI/GraphQL Schema contracts. Across the SDLC (requirements → design → implementation → test → deploy → maintenance), Agile/Scrum sprints last 1-2 weeks while DevOps teams practice continuous delivery.
Conclusion
PM2 with cluster mode is the gold standard for single-server Node.js production. Ten minutes of configuration gets you all cores, zero-downtime deploys, memory limits, log rotation and auto-restart. It is the natural last stop before moving to Kubernetes.
PM2, Docker and Kubernetes setups for Node.js applications Contact us