A Node.js process runs your JavaScript on one thread, so it uses roughly one CPU core for application code. On a 4-vCPU VPS, a single node server.js leaves most of the CPU idle under load. Cluster mode runs several copies of your app (one per core), and spreads incoming connections across them.

Option 1: PM2 cluster mode (easiest)

sudo npm install -g pm2
pm2 start server.js --name api -i max        # one instance per CPU core
# or a fixed number:
pm2 start server.js --name api -i 3
pm2 list
pm2 monit

With an ecosystem file:

// ecosystem.config.js
module.exports = {
  apps: [{
    name: "api",
    script: "server.js",
    instances: "max",          // or a number
    exec_mode: "cluster",
    max_memory_restart: "400M",
    env: { NODE_ENV: "production", PORT: 3000 },
  }],
};
pm2 start ecosystem.config.js
pm2 reload api            # zero-downtime: restarts instances one by one
pm2 save && pm2 startup

See PM2 process manager.

Option 2: the built-in cluster module

// cluster.js
const cluster = require("node:cluster");
const os = require("node:os");

if (cluster.isPrimary) {
  const n = Number(process.env.WORKERS) || os.availableParallelism();
  for (let i = 0; i < n; i++) cluster.fork();
  cluster.on("exit", (worker) => {
    console.log(`worker ${worker.process.pid} died, restarting`);
    cluster.fork();
  });
} else {
  require("./server");        // each worker runs the app and shares the listening port
}

PM2 does essentially this, plus monitoring, logs and graceful reloads.

Option 3: multiple containers or processes behind Nginx

Run several single-process instances (Docker replicas, or systemd template units on different ports), and load-balance with Nginx:

upstream api {
    least_conn;
    server 127.0.0.1:3001;
    server 127.0.0.1:3002;
    server 127.0.0.1:3003;
}

That’s useful when you want container-level isolation, or to scale across servers later. See Docker Compose scale and Nginx load balancing.

How many instances?

  • Start with one per vCPU. Leave a core for the database, Redis or Nginx if they share the VPS.
  • Each instance has its own memory (heap, caches), so RAM sets an upper limit. See how much RAM a VPS needs.
  • I/O-bound apps sometimes do fine with fewer instances. Measure with a load test. See load testing with k6.

Things that break in cluster mode

Instances don’t share memory. Anything stored in a process breaks when requests land on different instances:

Problem Fix
In-memory sessions Store sessions in Redis
In-memory caches Redis or a shared cache (or accept per-instance caches)
Scheduled jobs (setInterval, node-cron) run N times Run schedulers in one dedicated process, or use a job queue
WebSockets / Socket.IO Sticky sessions + the Socket.IO Redis adapter
Rate limiting in memory Use a Redis-backed limiter

Socket.IO needs sticky sessions (the same client always reaches the same instance) during the handshake: use @socket.io/sticky with the cluster module, ip_hash in Nginx for separate processes, or a PM2-compatible setup, plus @socket.io/redis-adapter to broadcast across instances.

Zero-downtime deploys

pm2 reload restarts instances one at a time, waiting for each to be ready. Implement graceful shutdown, so in-flight requests finish:

process.on("SIGINT", () => server.close(() => process.exit(0)));   // PM2 sends SIGINT
process.on("SIGTERM", () => server.close(() => process.exit(0)));  // Docker/systemd send SIGTERM

See zero-downtime Node.js deploys.

Alternatives to clustering

  • Worker threads for CPU-heavy tasks inside one process (image processing, parsing).
  • Background job queues (BullMQ with Redis) to move heavy work out of the request path. See background jobs and queues.

Quick reference

Task Command
Cluster with PM2 pm2 start app.js -i max
Reload without downtime pm2 reload <name>
Restart on high memory max_memory_restart: "400M"
Built-in node:cluster + cluster.fork()
Shared state Redis (sessions, cache, rate limits)
WebSockets Sticky sessions + Redis adapter

Related: Node.js memory leaks and Node.js environment variables.

  • Node.js
  • Performance
  • Scaling