The os Module
The built-in os module exposes information about the operating system and the machine Node.js is running on. It is the canonical way to ask questions like “which platform am I on?”, “how many CPU cores are available?”, or “how much free memory is left?” without shelling out to external commands. Because the answers come straight from the OS, os is invaluable for diagnostics, capacity planning, cross-platform branching, and health endpoints.
Everything in the module is synchronous and side-effect free, so it is cheap to call and safe to use during startup. Import it with the node: prefix:
import os from 'node:os';
If you are still on CommonJS, the equivalent is const os = require('node:os');. All of the functions below are available identically in both module systems.
Platform and architecture
os.platform() returns a short string identifying the operating system — 'linux', 'darwin' (macOS), 'win32' (Windows, even on 64-bit), and so on. os.arch() returns the CPU architecture Node.js was compiled for, such as 'x64' or 'arm64'. os.type() returns the raw kernel name from uname, while os.release() and os.version() give the kernel release and a descriptive version string.
import os from 'node:os';
console.log('platform:', os.platform());
console.log('arch: ', os.arch());
console.log('type: ', os.type());
console.log('release: ', os.release());
Output:
platform: linux
arch: x64
type: Linux
release: 6.8.0-45-generic
Use
os.platform()(the Node.js build target) rather than parsingprocess.argvor environment variables when you need to branch on the OS. For path separators specifically, prefer the path module over hand-rolled platform checks.
CPU information
os.cpus() returns an array with one object per logical CPU. Each entry includes the model string, the clock speed in MHz, and a times object breaking down milliseconds spent in user, nice, sys, idle, and irq states since boot. The length of the array is the most reliable way to size worker pools or thread counts.
import os from 'node:os';
const cpus = os.cpus();
console.log(`${cpus.length} logical cores`);
console.log('model:', cpus[0].model);
console.log('speed:', cpus[0].speed, 'MHz');
Output:
8 logical cores
model: Intel(R) Core(TM) i7-9700 CPU @ 3.00GHz
speed: 3000 MHz
os.availableParallelism() (Node.js 19.4+) is the recommended modern way to ask “how many tasks can I run in parallel?” It accounts for cgroup CPU limits inside containers, where os.cpus().length may overreport. os.loadavg() returns the 1-, 5-, and 15-minute load averages as an array of three numbers (always [0, 0, 0] on Windows).
import os from 'node:os';
const workers = os.availableParallelism();
console.log('parallelism:', workers);
console.log('load avg: ', os.loadavg());
Memory
os.totalmem() returns the total system memory in bytes, and os.freemem() returns the currently free memory in bytes. These describe the whole machine, not your Node.js process — for process-level numbers use process.memoryUsage() instead.
import os from 'node:os';
const gb = (bytes) => (bytes / 1024 ** 3).toFixed(2);
const total = os.totalmem();
const free = os.freemem();
const usedPct = ((1 - free / total) * 100).toFixed(1);
console.log(`total: ${gb(total)} GiB`);
console.log(`free: ${gb(free)} GiB`);
console.log(`used: ${usedPct}%`);
Output:
total: 15.55 GiB
free: 4.21 GiB
used: 72.9%
Host, user, and directories
These helpers describe the running environment and are handy for building log lines, temp file paths, and per-user config locations.
| Function | Returns |
|---|---|
os.hostname() | The machine’s network hostname |
os.homedir() | The current user’s home directory |
os.tmpdir() | The OS default directory for temporary files |
os.userInfo() | An object with username, uid, gid, shell, and homedir |
os.EOL | The platform line ending ('\n' or '\r\n') |
import os from 'node:os';
import path from 'node:path';
console.log('host: ', os.hostname());
console.log('home: ', os.homedir());
console.log('temp: ', os.tmpdir());
// Build a safe scratch file path
const scratch = path.join(os.tmpdir(), 'devcraftly-cache.json');
console.log('scratch:', scratch);
Output:
host: build-runner-3
home: /home/ada
temp: /tmp
scratch: /tmp/devcraftly-cache.json
Always derive temp paths from
os.tmpdir()rather than hard-coding/tmp. On Windows it resolves to something likeC:\Users\Ada\AppData\Local\Temp, and respecting it keeps your code portable.
Network interfaces
os.networkInterfaces() returns an object keyed by interface name (for example eth0, lo, en0). Each value is an array of address records containing address, family ('IPv4' or 'IPv6'), mac, internal, and cidr. A common task is finding your machine’s non-internal IPv4 address.
import os from 'node:os';
function getLocalIPv4() {
const interfaces = os.networkInterfaces();
for (const addresses of Object.values(interfaces)) {
for (const addr of addresses ?? []) {
if (addr.family === 'IPv4' && !addr.internal) {
return addr.address;
}
}
}
return '127.0.0.1';
}
console.log('LAN IP:', getLocalIPv4());
Output:
LAN IP: 192.168.1.24
Uptime
os.uptime() returns the number of seconds the system has been running since its last boot. It is a counter, not a timestamp, so format it yourself for human display.
import os from 'node:os';
function formatUptime(seconds) {
const d = Math.floor(seconds / 86400);
const h = Math.floor((seconds % 86400) / 3600);
const m = Math.floor((seconds % 3600) / 60);
return `${d}d ${h}h ${m}m`;
}
console.log('uptime:', formatUptime(os.uptime()));
Output:
uptime: 5d 13h 42m
This pairs naturally with os.loadavg(), os.freemem(), and os.availableParallelism() to assemble a compact /health or /metrics endpoint without any third-party dependencies.
Best practices
- Prefer
os.availableParallelism()overos.cpus().lengthwhen sizing worker pools, since it respects container CPU limits. - Use
os.tmpdir()andos.homedir()instead of hard-coded paths so your code works on Linux, macOS, and Windows. - Remember that
os.totalmem()/os.freemem()report the whole machine; useprocess.memoryUsage()for per-process accounting. - Treat
os.freemem()andos.loadavg()as point-in-time samples — poll them rather than reading once if you are monitoring trends. - Guard interface iteration with
addr.internalchecks and?? []to safely skip loopback and empty entries when scanningos.networkInterfaces(). - Branch on
os.platform()(the build target) rather than environment sniffing, and delegate path logic to thepathmodule.