Node.js Cheatsheet
Cluster (Multi-Core Servers)
Use this Node.js reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
What Cluster Does
node:cluster forks the same program into multiple worker processes that
share listening sockets — the standard way to use every CPU core for an HTTP
server without a load balancer in front. Workers are full processes (own
memory, own event loop); the primary distributes incoming connections
round-robin (except on Windows).
Clustered HTTP Server
import cluster from "node:cluster"; import http from "node:http"; import os from "node:os"; if (cluster.isPrimary) { const cpus = os.availableParallelism(); // prefer over os.cpus().length console.log(`primary ${process.pid} forking ${cpus} workers`); for (let i = 0; i < cpus; i++) cluster.fork(); // Restart crashed workers cluster.on("exit", (worker, code, signal) => { console.log(`worker ${worker.process.pid} died (${signal ?? code})`); cluster.fork(); }); } else { // Every worker calls listen(3000) — the primary shares the port http.createServer((req, res) => { res.end(`handled by ${process.pid}\n`); }).listen(3000); }
Primary ↔ Worker Messaging
// Primary const worker = cluster.fork(); worker.send({ cmd: "config", value: 42 }); worker.on("message", (msg) => console.log("from worker:", msg)); // Broadcast to all workers for (const id in cluster.workers) { cluster.workers[id].send({ cmd: "reload" }); } // Worker process.on("message", (msg) => { ... }); process.send({ ready: true });
Graceful Shutdown and Zero-Downtime Reload
// Primary: rolling restart — replace workers one at a time async function rollingRestart() { for (const worker of Object.values(cluster.workers)) { const replacement = cluster.fork(); await new Promise((r) => replacement.on("listening", r)); worker.disconnect(); // stop accepting, finish in-flight setTimeout(() => worker.kill(), 10_000).unref(); // hard deadline } } // Worker: close the server cleanly on disconnect process.on("disconnect", () => { server.close(() => process.exit(0)); });
Cluster API Reference
| API | Where | Purpose |
|---|---|---|
cluster.isPrimary | both | true in the original process |
cluster.isWorker | both | true in forked workers |
cluster.fork(env?) | primary | Spawn a worker (extra env vars merged in) |
cluster.workers | primary | { id: Worker } map of live workers |
cluster.worker | worker | This process's Worker handle |
worker.disconnect() | primary | Graceful: close server, exit when idle |
worker.kill(signal?) | primary | Force-terminate |
worker.isDead() | primary | Exited? |
cluster.on("exit" / "listening" / "online" / "message") | primary | Lifecycle events |
Cluster vs Alternatives
# Production processes are usually supervised externally instead: pm2 start app.js -i max # pm2 cluster mode (uses node:cluster inside) docker compose up --scale app=4 # one process per container + a proxy
- State does not survive across workers — sessions, caches, and rate-limit
counters must live in Redis/Postgres, not process memory.
- Sticky sessions (WebSocket + multi-worker) need an external LB or pm2's
sticky mode; the built-in round-robin is per-connection.
- For CPU-bound work inside one server, prefer worker_threads — cheaper than
a full process per task.