Use multiple CPU cores in Node.js

Choose processes for HTTP capacity or worker threads for CPU-bound JavaScript, and size concurrency to the workload and deployment limits.

Best practices
Illustration accompanying a Node.js development guide

A Node.js process normally runs application JavaScript on one main thread, while the runtime also uses other threads for selected operations. To use more CPU capacity, choose between multiple processes and worker threads according to the work you need to parallelize.

Multiple HTTP worker processes

The cluster module lets worker processes share a server port. This small example limits its default worker count to four for demonstration:

// cluster.cjs
const cluster = require('node:cluster');
const http = require('node:http');
const { availableParallelism } = require('node:os');

if (cluster.isPrimary) {
  const workers = Math.min(availableParallelism(), 4);
  for (let i = 0; i < workers; i++) cluster.fork();
  cluster.on('exit', (worker, code, signal) => {
    console.error(`Worker ${worker.process.pid} exited`, { code, signal });
  });
} else {
  http.createServer((req, res) => {
    res.end(`Handled by process ${process.pid}\n`);
  }).listen(3000, '127.0.0.1');
}

availableParallelism() is available from Node 18.14 and 19.4. Its estimate is a starting point, not a capacity benchmark. Account for container CPU limits, memory per worker, and other workloads before choosing a production count.

Cluster normally distributes connections through the primary process using round-robin scheduling, except for the default policy on Windows. It does not guarantee equal work or latency across workers. Workers have separate memory, so in-memory sessions, caches, and counters are not automatically shared. Use an appropriate shared store or explicit communication where coordination is required.

The example reports exits without unconditionally restarting them. A production supervisor needs restart backoff, failure limits, and a shutdown policy to avoid a crash loop.

Worker threads for CPU-heavy JavaScript

Worker threads execute JavaScript in separate threads within one process. This complete example uses the same file for the parent and worker:

// compute.cjs
const { Worker, isMainThread, parentPort, workerData } = require('node:worker_threads');

if (isMainThread) {
  const worker = new Worker(__filename, { workerData: 1000000 });
  worker.on('message', (result) => console.log(result));
  worker.on('error', (error) => {
    console.error(error);
    process.exitCode = 1;
  });
  worker.on('exit', (code) => {
    if (code !== 0) process.exitCode = 1;
  });
} else {
  let sum = 0;
  for (let i = 1; i <= workerData; i++) sum += i;
  parentPort.postMessage(sum);
}

Messages are generally cloned; data is not all shared automatically. Transferable buffers and SharedArrayBuffer support other patterns with their own ownership and synchronization requirements. For repeated jobs, use a worker pool instead of starting a new worker for every request.

Other deployment choices

PM2 cluster mode can start multiple workers with pm2 start app.js -i max. Its reload workflow still depends on application readiness, graceful shutdown, and configured deadlines; it is not an unconditional zero-downtime guarantee. Container deployments can instead run several replicas behind a service or load balancer. Avoid multiplying workers inside every replica without considering the resulting total.

Asynchronous I/O often handles many connections efficiently without CPU parallelism. Some native operations, including selected cryptographic and compression APIs, already use runtime threads. Measure the bottleneck before adding another pool.

See cluster, worker threads, and PM2 cluster mode.

Get a tailored plan

Contact us

Join the community

Scan with WeChat
to join the community

Try Guance

Start online and pay only for what you use.

Get started

Choose a Guance plan

Code hosting