DEV Community

Alex Aslam
Alex Aslam

Posted on

Node.js Clustering vs. Worker Threads: When to Fork and When to Thread

The Scaling Dilemma: More Cores, More Problems

Our payment processing API was peaking at 80% CPU usage on a 16-core server, yet only one core was doing most of the work. Node.js’s single-threaded nature was holding us back, but the solution wasn’t obvious:

  • Should we use clustering? (Fork processes)
  • Or Worker Threads? (Share memory, but not state)

We benchmarked both. Here’s what we learned.


Option 1: Clustering (Forking Processes)

How It Works

  • Master process forks multiple Node.js instances (one per CPU core).
  • Each process has its own memory, event loop, and V8 instance.

Best For:

Stateless APIs (e.g., REST services)
Crash isolation (one process dying doesn’t kill others)
Simple scaling (cluster module makes it easy)

The Catch:

  • Memory overhead (each fork duplicates memory)
  • No shared state (need Redis for inter-process comms)

Code Example:

const cluster = require('cluster');
const os = require('os');

if (cluster.isMaster) {
  for (let i = 0; i < os.cpus().length; i++) {
    cluster.fork(); // One process per CPU core
  }
} else {
  require('./server'); // Your actual app
}
Enter fullscreen mode Exit fullscreen mode

Our Result:

  • 4x throughput on a 4-core machine.
  • But memory usage doubled (each fork cached the same data).

Option 2: Worker Threads (Shared CPU, Not Memory)

How It Works

  • Single process, but offloads heavy work to threads.
  • Shares memory (via SharedArrayBuffer).

Best For:

CPU-heavy tasks (e.g., image processing, math ops)
Avoiding process duplication
Tasks needing shared memory

The Catch:

  • Thread-safe coding is hard (race conditions, deadlocks)
  • No automatic load balancing (unlike clustering)

Code Example:

const { Worker } = require('worker_threads');

function runWorker(data) {
  return new Promise((resolve) => {
    const worker = new Worker('./worker.js', { workerData: data });
    worker.on('message', resolve);
  });
}

// Use for heavy computations, not HTTP requests!
Enter fullscreen mode Exit fullscreen mode

Our Result:

  • 30% faster for CPU tasks (vs clustering).
  • But crashed once due to a race condition.

When to Use Which?

Scenario Clustering Worker Threads
Stateless HTTP server ✅ Best ❌ Not needed
Heavy computations ❌ Overkill ✅ Best
Need crash isolation ✅ Yes ❌ No
Shared memory required ❌ No ✅ Yes

Key Takeaways

Clustering = Simple scaling for APIs.
Worker Threads = CPU tasks without forking.
Hybrid? Sometimes both (e.g., cluster + threads for CPU-heavy APIs).

We combined both—clustering for HTTP load balancing and threads for PDF generation.

Have you tried both? What worked (or backfired)?

Top comments (0)