systeminformation
systeminformation is a server-side Node library that reports hardware and operating system detail that Node's built-in os module does not expose: CPU model and per-core load, memory layout, physical disks and S.M.A.R.T. status, filesystems, GPUs, battery, USB and audio devices, network interfaces and per-second throughput, running processes and services, wifi, bluetooth, printers, and Docker containers. It has no npm dependencies, ships its own TypeScript definitions, and every function returns a promise (or takes a callback). Under the hood most values come from parsing the output of platform command line tools, which is the thing to understand before you rely on it.
The most complete way to read machine state from Node, and worth it when you need the breadth. Treat every field as optional, test on each OS you ship to, and cache the expensive calls, because the numbers come from command output rather than a stable system API.
Use it if
- You are building a monitoring agent, admin dashboard, or status endpoint in Node and need per-core load, disk usage, and network throughput
- You need hardware detail beyond os.cpus() and os.totalmem(), such as disk model and serial, GPU controllers, battery health, or memory stick layout
- You ship an Electron or Bun desktop app and want the same system-specs call to work on Windows, macOS, and Linux
- You want Docker container and image stats without pulling in a full Docker client library
- You need this in a browser: the README states plainly that it will not work there, and the package is backend only
- You run in a slim container or hardened host: most values are scraped from external binaries such as powershell, lm-sensors, smartmontools, and system_profiler, and when those are missing you get empty strings and nulls rather than an error you can catch
- You call it on a hot request path: each function spawns child processes, so polling several of them per second costs real CPU on the machine you are trying to measure
- You need consistent fields across platforms: the reference table marks most properties per OS, temperature needs a separate package on macOS and elevated rights on Windows, and BSD support is thin
- You want API stability for the next few years: the author has announced a version 6 rewritten in TypeScript with a cleaned-up API and explicit breaking changes
- You expect throughput numbers on the first call: networkStats, fsStats, and disksIO return null rate fields until the second invocation, because rates are computed between two calls
Setup reality
npm i systeminformation installs one package with no dependencies and bundled TypeScript types, and the first si.cpu() works. The gap between that and production is the external tooling. Windows 11 dropped wmic, so the library moved to powershell and wants version 5 or newer; older Windows can still show encoding problems on non-ASCII values. Linux temperature needs lm-sensors installed and S.M.A.R.T. needs smartmontools. macOS temperature needs you to also install macos-temperature-sensor on Apple Silicon or osx-temperature-sensor on Intel, because the optional dependency was dropped to avoid install warnings elsewhere. Several Windows values only return anything when the process has admin rights. The package is CommonJS, so an ESM project gets the whole namespace through a default import. Calls that gather everything, such as getStaticData, can take tens of seconds on first run, so cache the static half instead of calling it per request.
Patterns
Read CPU and OS basicsbasic-system-info
const si = require('systeminformation')
const [cpu, os] = await Promise.all([si.cpu(), si.osInfo()])
console.log(cpu.manufacturer, cpu.brand, cpu.physicalCores, cpu.cores)
console.log(os.platform, os.distro, os.release, os.arch)Every function except version() and time() is async. In an ESM file use import si from "systeminformation" as a default import, because the package is CommonJS.
Get CPU load overall and per corecurrent-load
const load = await si.currentLoad()
console.log(load.currentLoad.toFixed(1) + '%')
load.cpus.forEach((c, i) => console.log(`core ${i}: ${c.load.toFixed(1)}%`))currentLoad is computed from tick deltas since the previous call, so the very first reading covers the time since boot and looks unnaturally flat. Poll twice and use the second value.
Memory, including the value you actually want on Linuxmemory-usage
const mem = await si.mem()
const usedGb = (mem.active / 1024 ** 3).toFixed(2)
console.log({ total: mem.total, free: mem.free, used: mem.used, active: mem.active })On Linux mem.used counts buffers and cache, so it always looks near 100%. mem.active is the number a dashboard should show.
Filesystem usage per mountdisk-usage
const disks = await si.fsSize()
for (const d of disks) {
console.log(d.mount, d.type, d.use.toFixed(1) + '%', d.size, d.available)
}
// a single drive only
const root = await si.fsSize('/')In containers this lists the overlay and every bind mount, so filter by mount before rendering. Network mounts that are hung will make the call block until the underlying command times out.
Measure network throughput per secondnetwork-throughput
const iface = (await si.networkInterfaces('default')).iface
await si.networkStats(iface) // first call primes the counters
setInterval(async () => {
const [s] = await si.networkStats(iface)
console.log(s.rx_sec, s.tx_sec)
}, 1000)rx_sec and tx_sec are null on the first call by design, because rates are derived from the delta between two calls. The README spells this out for networkStats, fsStats, and disksIO.
List the heaviest processestop-processes
const { all, running, list } = await si.processes()
const top = list
.sort((a, b) => b.cpu - a.cpu)
.slice(0, 10)
.map((p) => ({ pid: p.pid, name: p.name, cpu: p.cpu, mem: p.mem }))
console.log({ all, running, top })This is one of the slowest calls in the library because it enumerates every process. Do not put it behind an endpoint that can be hit repeatedly without a cache.
Read CPU temperature where it is availablecpu-temperature
const temp = await si.cpuTemperature()
if (temp.main === null || temp.main === -1) {
console.log('no temperature sensor available on this host')
} else {
console.log(temp.main, temp.cores, temp.max)
}Linux needs lm-sensors installed, Windows often needs admin rights, and macOS returns nothing unless you separately install macos-temperature-sensor on Apple Silicon or osx-temperature-sensor on Intel.
Fetch exactly the fields you need in one callpartial-snapshot
const data = await si.get({
cpu: 'manufacturer, brand, speed',
mem: 'total, active',
osInfo: 'platform, distro, release',
currentLoad: 'currentLoad',
fsSize: '*',
})si.get is much cheaper than getAllData because it only runs the collectors you name. Use "*" for a whole section and a comma separated list otherwise.
Poll a value object and react only to changesobserve-changes
si.observe(
{ currentLoad: 'currentLoad', mem: 'active' },
2000,
(data) => {
pushToDashboard(data)
},
)observe polls on your interval and calls back when the result differs from the previous poll. Pick an interval well above the time the underlying commands take, or polls will overlap.
Inspect Docker containers and their statsdocker-containers
const info = await si.dockerInfo()
console.log(info.containersRunning, info.images)
const containers = await si.dockerContainers(true) // true includes stopped
for (const c of containers) {
const [stats] = await si.dockerContainerStats(c.id)
console.log(c.name, c.state, stats.cpuPercent, stats.memUsage)
}These talk to the Docker socket, so the Node process needs access to it. Inside a container that means mounting /var/run/docker.sock, which is effectively root on the host.
Split the expensive static data from the cheap dynamic datastatic-vs-dynamic
// once at boot, cache the result
const staticData = await si.getStaticData()
// per poll
const dynamic = await si.getDynamicData('*', 'eth0')getStaticData walks hardware inventory and the README warns it can take up to 30 seconds. Calling it per request is the most common way people make this library look slow.
Physical disks, GPUs, and machine identityhardware-inventory
const [layout, gpu, uuid] = await Promise.all([
si.diskLayout(),
si.graphics(),
si.uuid(),
])
console.log(layout.map((d) => [d.name, d.type, d.size, d.smartStatus]))
console.log(gpu.controllers.map((c) => c.model))
console.log(uuid.os, uuid.hardware)smartStatus needs smartmontools on Linux and macOS and reports "unknown" without it. uuid.hardware is empty on many virtual machines, so do not use it alone as a licence key.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| node-os-utils | npm | You only need CPU, memory, disk, and network basics and want a much smaller surface to reason about. |
| pidusage | npm | You care about CPU and memory of specific processes rather than the whole machine. |
| dockerode | npm | Docker is the actual subject and you want the full Engine API instead of a summary of containers. |