Skip to content
Node.js nd core 4 min read

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 parsing process.argv or 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.

FunctionReturns
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.EOLThe 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 like C:\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() over os.cpus().length when sizing worker pools, since it respects container CPU limits.
  • Use os.tmpdir() and os.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; use process.memoryUsage() for per-process accounting.
  • Treat os.freemem() and os.loadavg() as point-in-time samples — poll them rather than reading once if you are monitoring trends.
  • Guard interface iteration with addr.internal checks and ?? [] to safely skip loopback and empty entries when scanning os.networkInterfaces().
  • Branch on os.platform() (the build target) rather than environment sniffing, and delegate path logic to the path module.
Last updated June 14, 2026
Was this helpful?