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?

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);
}

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.

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."

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.

Node.js production deploys

PM2, Docker and Kubernetes setups for Node.js applications Contact us

WhatsApp