Logo

Enabling Multi-CPU Usage in Node.js

Node.js

The Problem

When traffic spiked, my app started crashing 😅 Despite running in a pod with 2 CPU cores, it was happily chugging along on just one. The other core? Just sitting there, sipping tea or whatever idle cores do.

Turns out, Node.js doesn’t automatically use multiple cores, so even though more resources were available, the app hit its limit and gave up.

Here’s how I scaled my app across multiple cores using the built-in cluster module, plus a quick dive into why this approach isn’t true multithreading—and why that’s perfectly fine.

🔧 The setup

App Side

Use the cluster module to spin up multiple Node.js processes — each one gets its own core.

1import cluster from 'cluster'
2import os from 'os'
3
4const defaultProcessCount = os.cpus().length
5const clusterSize = process.env.PROCESS_COUNT ?? defaultProcessCount
6
7if (cluster.isPrimary) {
8 for (let i = 0; i < clusterSize; i++) {
9 cluster.fork()
10 }
11
12 cluster.on('exit', (worker) => {
13 console.log(`worker ${worker.process.pid} died`)
14 })
15} else {
16 // app logic goes here
17}

Kubernetes Side

On the infra side (e.g. Kubernetes), I made sure to assign enough CPUs by adding this to the deployment:

1resources:
2 limits:
3 cpu: '2'
4 memory: '2048Mi'

💡 Make sure to match the number of processes with the available CPU cores. If 4 processes are forked but only 1 CPU is given to the pod, they’ll just fight over that single core.

🧠 multi-process ≠ multi-threaded

Using cluster means multi-process, not multi-threaded.

Each forked worker is a completely separate process. They don’t share memory, meaning memory usage will increase with each process.

This is why it’s still not as efficient as something like Go or Java, where threads are managed inside the same process and share memory space.

Still, for web apps with lots of I/O, this kind of parallelism works really well.

💭 Why JS is single-threaded

“JavaScript was designed to be single-threaded so that developers could write code without worrying about race conditions and thread safety.” — MDN Web Docs

Since JS was originally made for the browser, the single-thread model kept things simple. If it were multi-threaded, the DOM would be a nightmare to manage with concurrent updates. Instead, async behavior is handled via the event loop — which is why async/await and setTimeout feel so natural.

✅ wrap-up

  • cluster.fork() = multiple Node.js processes
  • But not real threads — memory isn’t shared
  • Update CPU limits in Kubernetes to match how many processes I wanna run
  • JS is single-threaded by design, and I’m cool with that