Skip to content

Latest commit

 

History

History
239 lines (188 loc) · 9.63 KB

File metadata and controls

239 lines (188 loc) · 9.63 KB

Node.js Runtime Management

Overview

Node.js is a JavaScript runtime used to build, execute, and operate command-line tools, backend services, build systems, and modern web applications. Every project depending on Node.js also depends on a specific runtime release, on the npm client shipped with it, and on the binary interface exposed to compiled native modules.

Runtime management is an ownership problem before it is a version problem. A workstation that installs Node.js through Homebrew, a version manager, and a container image at once will eventually resolve an executable nobody intended, and the failure is usually reported as a dependency error rather than an installation error.

This document establishes a single owner for the installation and covers resolution, upgrades, version files, release lines, and the work required after the runtime changes. Keeping supported releases current provides security fixes, framework compatibility, and modern language features, but updates must be tested, because major versions may introduce breaking changes and invalidate compiled native modules. Dependency installation, auditing, and registry trust are covered in npm dependency management.

Choose a Single Owner for the Installation

Use one clear owner for the Node.js installation. Do not mix management methods for the same active executable.

Owner Appropriate when Principal trade-off
Homebrew One system-wide version suffices Upgrades follow the formula
Version manager Projects need different releases Needs shell integration
Containers The runtime must be isolated Slower local iteration

Homebrew suits projects that converge on one release line; a version manager suits projects that pin different releases, including releases older than the current formula; containers suit work where the runtime and build toolchain must match a deployment target. Record the selected owner in project documentation, since an undocumented choice is rediscovered by every contributor during an incident.

Confirm Which Executable Resolves

Determine which executable the shell resolves before installing or upgrading anything. command -v reports the path selected for the current session, and type -a lists every match on PATH in resolution order, where the first entry is the one that runs. Because version managers and Homebrew publish shims and symbolic links, process.execPath is the reliable answer to where the running runtime lives. The last two commands report the active versions; node -v is the shorter equivalent of node --version.

command -v node
type -a node
node -p "process.execPath"
node --version
npm --version

Install and Upgrade with Homebrew

Install the current formula, which includes a compatible npm release. Review the release notes for the target major version before upgrading, because the formula tracks the current release line and may cross a major boundary. Verify both tools afterwards, since npm is replaced along with the runtime.

brew install node
brew upgrade node

Homebrew also publishes versioned formulae for individual release lines. These may be keg-only, meaning they are not linked into the prefix and are not on PATH by default, so inspect the caveats before assuming they are active.

brew install node@22
brew info node@22

Update npm for the Active Runtime

npm can be upgraded independently of the runtime that ships it. Inspect the requirements of the target release before installing it globally, since an incompatible npm client is awkward to recover from. Under a version manager, an upgrade for one release does not apply to the others.

npm view npm@latest engines
npm install --global npm@latest

Use a Version Manager

A version manager allows projects to use different releases without replacing the system-wide runtime. Select one tool, document the choice, and remove the others, because two managers competing for the same shell initialization produce non-deterministic resolution.

Tool Select a release Project version file
nvm nvm install 24 then nvm use 24 .nvmrc
fnm fnm install 24 then fnm use 24 .nvmrc or .node-version
mise mise use node@24 mise.toml
asdf asdf install nodejs <version> .tool-versions

asdf requires the Node.js plugin, added with asdf plugin add nodejs, and the subcommand used to pin a version differs between asdf major versions. Consult the upstream documentation for the installed release rather than copying commands from older guides.

Every version manager requires shell integration to switch releases per directory, and each provides a way to set the default release used by new shells, such as nvm alias default 24. Global packages and the npm client are typically isolated per release, so a tool installed globally under one release is unavailable under another.

Record the Expected Release

Version Files

A version file states the release a project expects and lets a version manager select it without arguments, for example nvm use in the project directory. nvm reads .nvmrc; .node-version is read by several tools and is the more portable choice. Either file contains the release alone.

24

The engines Field

The engines field in package.json declares the runtime range a project supports. Review it before changing the runtime.

{
  "engines": {
    "node": ">=22 <26",
    "npm": ">=10 <12"
  }
}

By default npm treats engines as advisory and emits an EBADENGINE warning when the active runtime falls outside the declared range. To make the mismatch a hard failure, enable strict engine checking in the project .npmrc.

engine-strict=true

The two mechanisms are complementary: the version file selects a release locally, while the range enforces the supported window for every consumer.

Release Lines and Long Term Support

Node.js publishes a new major release every six months. Even-numbered majors are promoted to Long Term Support; odd-numbered majors never are and reach end of life relatively quickly.

Phase Meaning Suitable for
Current Newest line, receives new features Compatibility testing
Active LTS Even-numbered line under long term support Production systems
Maintenance Critical and security fixes only Systems awaiting upgrade
End of Life No further releases, including security fixes Nothing; migrate

Prefer an Active LTS or Maintenance release for production unless the project documents a reason to use another line. Treat the end of life date as scheduled work rather than an event to react to, because no fixes are issued after it.

Rebuild Native Modules After a Runtime Change

Native modules are compiled against the Node.js binary interface, identified by an ABI version. Changing the runtime major version changes that ABI and invalidates compiled artifacts, producing a NODE_MODULE_VERSION mismatch at require time. Inspect the ABI version and recompile native modules against it. When the environment may hold stale artifacts, prefer the clean reinstall, which removes node_modules before installing from the lock file.

node -p "process.versions.modules"
npm rebuild
npm ci

Compiling native modules on macOS requires the Command Line Tools. If a rebuild fails with a missing compiler or missing headers, verify the developer toolchain before investigating the package.

Development Best Practices

  • Select one owner for the Node.js installation and document the choice.
  • Confirm the resolved executable before diagnosing a version problem.
  • Prefer an Active LTS release for production systems.
  • Record runtime expectations through a version file and the engines field.
  • Track end of life dates and schedule major upgrades in advance.
  • Rebuild or reinstall native modules after any runtime major version change.
  • Validate builds, tests, and type checks after a runtime change.
  • Remove unused version managers rather than leaving them in shell startup.

Troubleshooting

Inspect the active runtime, the npm client, and the prefix where global packages are installed; a prefix outside the expected owner indicates a mixed installation. When the reported version is unexpected, list every node on PATH and print the search path, since version manager shims must precede Homebrew paths for per-directory switching to take effect.

node --version
npm --version
npm config get prefix
type -a node
printf '%s\n' "$PATH"

A require-time NODE_MODULE_VERSION error means the artifacts were compiled for a different runtime; rebuild or reinstall from the lock file rather than editing the dependency tree. An EBADENGINE warning means the runtime falls outside the declared range; change the runtime rather than widening the range.

Avoid deleting lock files or caches as a first troubleshooting step. Diagnose the ownership, version, and dependency constraint causing the failure before discarding reproducibility information.

Related Documentation