<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/node-js-thread-pools-on-busy-machines-8qrelskew" -->

---
title: Node.js Thread Pools on Busy Machines | daily.dev
description: An argument against automatically sizing Node.js's libuv thread pool based on CPU count, responding to a proposal (Nub's PR) that would do so using...
canonical: https://daily.dev/posts/node-js-thread-pools-on-busy-machines-8qrelskew
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Node.js Thread Pools on Busy Machines | daily.dev
og:description: An argument against automatically sizing Node.js's libuv thread pool based on CPU count, responding to a proposal (Nub's PR) that would do so using...
og:url: https://daily.dev/posts/node-js-thread-pools-on-busy-machines-8qrelskew
og:image: https://api.daily.dev/og/posts/8qReLSkew.png
og:image:alt: Node.js Thread Pools on Busy Machines
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Node.js Thread Pools on Busy Machines

**[Platformatic](https://daily.dev/sources/platformatic)** · 4 min read · 49 upvotes · 4 comments

## Summary

An argument against automatically sizing Node.js's libuv thread pool based on CPU count, responding to a proposal (Nub's PR) that would do so using os.availableParallelism(). The author contends that available parallelism doesn't mean idle CPUs, so scaling worker count on a busy host can worsen event-loop latency for CPU-bound work like bcrypt hashing, even though nice-value prioritization helps reduce interference with neighboring processes. A one-vCPU gzip benchmark shows four concurrent workers performing worse than one (37.3 MiB/s vs 49.3 MiB/s), while I/O-bound waiting workloads behave differently from CPU-bound ones since they don't compete for compute. The conclusion favors manual, measured pool sizing over automatic defaults, testing on busy hosts and bounding queues rather than just adding threads.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://blog.platformatic.dev/more-threads-on-an-already-busy-machine>

## Questions this post answers

### Why does os.availableParallelism() give a misleading thread pool size on a busy server?

Because it reports how many CPUs the process could use under OS constraints, not whether those CPUs are actually idle. On a busy machine, multiple processes each seeing the same core count will size their thread pools independently and end up competing for the same CPU time, which can slow down CPU-bound jobs like bcrypt hashing rather than speed them up.

_Anyone tuning Node.js concurrency under real server load can track this kind of runtime nuance on daily.dev._

### Does adding more thread pool workers always improve Node.js throughput under CPU-bound load?

No. A one-vCPU gzip test with 2 MiB inputs and 8:1 compression showed one worker with one outstanding job reaching 49.3 MiB/s, while four workers with four outstanding jobs dropped to 37.3 MiB/s. Increasing both worker count and concurrent jobs made compression slower in that CPU-constrained container scenario, though this came from short synthetic Docker Desktop trials.

_Developers deciding on worker pool sizing for containerized Node.js apps can follow benchmarks like this on daily.dev._

### What nice value does Node.js assign to thread pool workers beyond the first four, and does it fix CPU contention?

Workers beyond the original four get Linux nice value 10, which reduces interference with neighboring processes on a busy host but does not reserve idle CPU time for them. The scheduler still grants those lower-priority workers a share of CPU time when the host is busy, so a bcrypt job running on one can still take longer to complete, trading neighbor protection for worse request latency.

_Teams debugging request latency from background worker priority can keep up with details like this via daily.dev._

## Community discussion

Top comments from developers on daily.dev.

**@jenueldev** · 10 upvotes

> Treating availableParallelism as spare capacity is the subtle mistake here. A safer default would be conservative pool sizing plus bounded queues and workload-specific telemetry, because CPU-heavy bcrypt and mostly waiting I/O jobs need different concurrency budgets. The one-vCPU gzip result is a useful reminder to benchmark under contention, not on an idle laptop.

**@agustinbarrientos** · 0 upvotes

> Have you measured filesystem tail latency with a separate admission limit for bcrypt?

**@ristotoldsep** · 0 upvotes

> CPU count isn’t spare CPU. Benchmarking on a busy box matters.

## Similar posts on daily.dev

- [Stop Calling Node.js “Single-Threaded”: A Look Under the Hood](https://daily.dev/posts/stop-calling-node-js-single-threaded-a-look-under-the-hood-ofsmhbjmm) · Medium · 6 upvotes · 0 comments
- [Slint and the Node.js Event Loop](https://daily.dev/posts/slint-and-the-node-js-event-loop-3ayskjayl) · Slint · 78 upvotes · 1 comments
- [Can't We Be Friends?](https://daily.dev/posts/can-t-we-be-friends--asruf1dhd) · RUBYLAND · 0 upvotes · 0 comments
- [Unpacking Cloudflare Workers CPU Performance Benchmarks](https://daily.dev/posts/unpacking-cloudflare-workers-cpu-performance-benchmarks-myvugzoix) · Cloudflare · 6 upvotes · 0 comments

---

Tags: [#nodejs](https://daily.dev/tags/nodejs)

[View this post on daily.dev](https://daily.dev/posts/node-js-thread-pools-on-busy-machines-8qrelskew)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"Node.js Thread Pools on Busy Machines","url":"https://daily.dev/posts/node-js-thread-pools-on-busy-machines-8qrelskew","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/node-js-thread-pools-on-busy-machines-8qrelskew"},"datePublished":"2026-09-24T20:07:15.942Z","dateModified":"2026-09-24T20:07:41.084Z","description":"An argument against automatically sizing Node.js's libuv thread pool based on CPU count, responding to a proposal (Nub's PR) that would do so using...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/dca196f22200ace6f8cdf5ca0eaa45e5?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/dca196f22200ace6f8cdf5ca0eaa45e5?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"Platformatic","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Platformatic","logo":"https://media.daily.dev/image/upload/s--mtLTNLFl--/f_auto/v1721999416/logos/platformatic","url":"https://daily.dev/sources/platformatic"},"commentCount":4,"discussionUrl":"https://daily.dev/posts/node-js-thread-pools-on-busy-machines-8qrelskew","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":49},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":4}],"keywords":"nodejs","timeRequired":"PT4M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Platformatic","item":"https://daily.dev/sources/platformatic"},{"@type":"ListItem","position":3,"name":"Node.js Thread Pools on Busy Machines"}]}
{"@context":"https://schema.org","@type":"WebPage","@id":"https://daily.dev/posts/node-js-thread-pools-on-busy-machines-8qrelskew","comment":[{"@type":"Comment","text":"Treating availableParallelism as spare capacity is the subtle mistake here. A safer default would be conservative pool sizing plus bounded queues and workload-specific telemetry, because CPU-heavy bcrypt and mostly waiting I/O jobs need different concurrency budgets. The one-vCPU gzip result is a useful reminder to benchmark under contention, not on an idle laptop.","datePublished":"2026-09-25T04:05:29.577Z","url":"https://daily.dev/posts/8qReLSkew#c-lrGQU95ut","author":{"@type":"Person","name":"Jenuel Oras Ganawed","url":"https://daily.dev/jenueldev","image":"https://media.daily.dev/image/upload/s--BadmiNyj--/f_auto/v1787625077/avatars/avatar_JJtZr4Sjm?_a=BAMAMicg0"},"interactionStatistic":{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":10}},{"@type":"Comment","text":"Have you measured filesystem tail latency with a separate admission limit for bcrypt?","datePublished":"2026-09-27T19:59:09.360Z","url":"https://daily.dev/posts/8qReLSkew#c-Xa9c3LJ9f","author":{"@type":"Person","name":"Agustin Barrientos","url":"https://daily.dev/agustinbarrientos","image":"https://media.daily.dev/image/upload/s--5ayxQnqn--/f_auto/v1788281802/avatars/avatar_wQYYVe5Tbj0NJ7C7qPoa8?_a=BAMAMicg0"}},{"@type":"Comment","text":"CPU count isn’t spare CPU. Benchmarking on a busy box matters.","datePublished":"2026-09-28T09:42:12.217Z","url":"https://daily.dev/posts/8qReLSkew#c-rPE6nQIO4","author":{"@type":"Person","name":"Risto Tõldsep","url":"https://daily.dev/ristotoldsep","image":"https://lh3.googleusercontent.com/a/ACg8ocLDWc6mZn0JwNmXXw6WY0L_HJ6pegzRttooC5VgtXESHSj3MNYx=s96-c"}}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/node-js-thread-pools-on-busy-machines-8qrelskew#faq","mainEntity":[{"@type":"Question","name":"Why does os.availableParallelism() give a misleading thread pool size on a busy server?","acceptedAnswer":{"@type":"Answer","text":"Because it reports how many CPUs the process could use under OS constraints, not whether those CPUs are actually idle. On a busy machine, multiple processes each seeing the same core count will size their thread pools independently and end up competing for the same CPU time, which can slow down CPU-bound jobs like bcrypt hashing rather than speed them up. Anyone tuning Node.js concurrency under real server load can track this kind of runtime nuance on daily.dev."}},{"@type":"Question","name":"Does adding more thread pool workers always improve Node.js throughput under CPU-bound load?","acceptedAnswer":{"@type":"Answer","text":"No. A one-vCPU gzip test with 2 MiB inputs and 8:1 compression showed one worker with one outstanding job reaching 49.3 MiB/s, while four workers with four outstanding jobs dropped to 37.3 MiB/s. Increasing both worker count and concurrent jobs made compression slower in that CPU-constrained container scenario, though this came from short synthetic Docker Desktop trials. Developers deciding on worker pool sizing for containerized Node.js apps can follow benchmarks like this on daily.dev."}},{"@type":"Question","name":"What nice value does Node.js assign to thread pool workers beyond the first four, and does it fix CPU contention?","acceptedAnswer":{"@type":"Answer","text":"Workers beyond the original four get Linux nice value 10, which reduces interference with neighboring processes on a busy host but does not reserve idle CPU time for them. The scheduler still grants those lower-priority workers a share of CPU time when the host is busy, so a bcrypt job running on one can still take longer to complete, trading neighbor protection for worse request latency. Teams debugging request latency from background worker priority can keep up with details like this via daily.dev."}}]}
```

