os.cpus()
Returns an array describing each logical CPU core, including its model, speed and cumulative time counters.
os.cpus()- Added in Node.js v0.3.3
Return value
Object[]An array with one object per logical CPU core, each holding `model` (string), `speed` (number, in MHz) and `times` (an object of `user`, `nice`, `sys`, `idle` and `irq` millisecond counters). The array is empty when no CPU information is available.
os.cpus() is the descriptive view of the machine's processors. Each entry is one logical core, so a 6-core CPU with hyper-threading reports 12 entries, and the times counters are cumulative milliseconds since boot rather than a live percentage.
When to reach for it
Use it when you want to report hardware — a diagnostics endpoint, a bug-report banner, a benchmark header. Use it to compute CPU utilisation by sampling times twice and dividing the delta of idle by the delta of the total; a single reading tells you nothing about current load, only the average since the machine started.
What trips people up
Do not size a worker pool with os.cpus().length. Inside a container with a CPU quota, or on a process restricted by a CPU affinity mask, it still reports every core the kernel can see, so you spawn far more workers than you have CPU to run them. os.availableParallelism() exists precisely for that job.
The array can legitimately be empty when /proc is unavailable, so guard indexing into cpus[0]. speed is often 0 on ARM Linux and on some virtualised hosts, and nice is always 0 on Windows.
Example
When to use it
- Print core count and CPU model into a diagnostics or crash report
- Compute per-core utilisation by sampling times twice a second apart
- Log hardware details alongside benchmark results so runs can be compared
Frequently asked questions
Why does os.cpus().length return more cores than my container has?
Because os.cpus() reads the kernel's list of logical processors and ignores cgroup CPU quotas entirely. A container limited to 2 CPUs on a 64-core host still reports 64 entries. Call os.availableParallelism() instead, which honours the quota and the process affinity mask.
How do I get current CPU usage percentage in Node.js?
Take two os.cpus() snapshots a known interval apart and compare the times counters, since a single call reports totals accumulated since boot. For each core, sum user, nice, sys, idle and irq to get the total, then busy percentage is (1 - deltaIdle / deltaTotal) * 100.
Why is the speed property 0 for every core?
Because the platform does not expose a clock frequency to libuv, which is common on ARM Linux, inside virtual machines and on many cloud instances. The value is informational only; treat 0 as "unknown" and hide the field rather than reporting a 0 MHz processor.
More examples
Measure real CPU utilisation over an interval
The output shows that a single call to os.cpus() is useless for load: you need two snapshots. The delta of idle divided by the delta of total gives the share of the interval each core spent busy, so the loop above pushes one core close to 100% while the average stays low.
const os = require('node:os');
function snapshot() {
return os.cpus().map((c) => {
const t = c.times;
const total = t.user + t.nice + t.sys + t.idle + t.irq;
return { total, idle: t.idle };
});
}
const before = snapshot();
// Burn some CPU so the delta is non-zero.
const until = Date.now() + 300;
let n = 0;
while (Date.now() < until) n += Math.sqrt(n + 1);
const after = snapshot();
const busy = after.map((a, i) => {
const dTotal = a.total - before[i].total;
const dIdle = a.idle - before[i].idle;
return dTotal > 0 ? (1 - dIdle / dTotal) * 100 : 0;
});
console.log('cores sampled:', busy.length);
console.log('busiest core:', Math.max(...busy).toFixed(1) + '%');
console.log('average busy:', (busy.reduce((a, b) => a + b, 0) / busy.length).toFixed(1) + '%');Summarise the machine without dumping the array
Grouping by model collapses a long array into one line and shows big.LITTLE machines, where the entries split into two model groups. The last line demonstrates that speed is frequently 0 on ARM and virtualised hosts, so it is not safe to display unconditionally.
const os = require('node:os');
const cpus = os.cpus();
const models = new Map();
for (const c of cpus) models.set(c.model, (models.get(c.model) ?? 0) + 1);
console.log('distinct CPU models:', models.size);
console.log('cores per model:', [...models.values()]);
console.log('reported speeds are numbers:', cpus.every((c) => typeof c.speed === 'number'));
console.log('any speed missing (0):', cpus.some((c) => c.speed === 0));Guard against an empty array
os.cpus() returns an empty array when /proc is not mounted, so cpus[0].model throws a TypeError in exactly the restricted environments where you most want diagnostics. The final two lines print side by side the number you should display and the number you should size a pool with.
const os = require('node:os');
function describeCpu() {
const cpus = os.cpus();
if (cpus.length === 0) return 'CPU information unavailable';
return `${cpus.length} x ${cpus[0].model.trim()}`;
}
console.log(describeCpu());
// Sizing a pool is a different question — use availableParallelism().
console.log('cores visible:', os.cpus().length);
console.log('parallelism to use:', os.availableParallelism());
Discussion