Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

nvm — Administrator & Developer Guide

Generated May 2026 from nvm-sh/nvm at f695512

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

nvm v0.40.4 is a POSIX-compliant bash shell function for managing multiple concurrent Node.js installations. It is not a daemon, not a binary, and has no server component: the entire tool is a single bash function sourced into the user's shell on login.


1. Architecture Overview

How nvm Works

nvm is implemented as a shell function (not an executable) defined in nvm.sh. Sourcing the file adds the nvm function — and ~120 helper functions — to the current shell's environment. All state is represented as files on disk; there is no background process, no daemon, and no IPC.

Shell Session
└── source ~/.nvm/nvm.sh
    ├── nvm()              — main dispatch function
    ├── nvm_ls()           — local version scanning
    ├── nvm_ls_remote()    — remote index fetch (nodejs.org)
    ├── nvm_install_binary() / nvm_install_node_source()
    └── ...~120 helper functions

Filesystem State
~/.nvm/
├── nvm.sh              — main script (sourced by shell)
├── nvm-exec            — exec wrapper (executable)
├── bash_completion     — tab-completion definitions
├── alias/
│   ├── default         — "default" alias file
│   ├── lts/
│   │   ├── *           — lts/* alias
│   │   └── iron        — lts/iron alias
│   └── <custom>        — user-created aliases
├── versions/
│   ├── node/
│   │   ├── v20.11.0/   — Node.js installation
│   │   └── v22.4.0/
│   └── io.js/          — io.js installations (legacy)
└── .cache/
    └── bin/            — downloaded binary cache

Note

All aliases are plain text files: the file name is the alias name and the file content is the target version string. This makes aliases trivially auditable and scriptable.

PATH Manipulation

nvm use <version> modifies $PATH by prepending $NVM_DIR/versions/node/<version>/bin and removing any previously prepended nvm path. It does not modify system paths. The effect is scoped to the current shell process.

Version Resolution

nvm resolves version strings through a chain:

  1. Exact match against installed versions in $NVM_DIR/versions/
  2. Alias resolution (reads $NVM_DIR/alias/<name>)
  3. Implicit alias resolution (node → latest installed, lts/* → latest lts alias)
  4. Partial version expansion (e.g., 22 → latest installed v22.x.x)

Remote resolution queries $NVM_NODEJS_ORG_MIRROR/index.tab (defaults to https://nodejs.org/dist/index.tab).


2. Component Inventory

File Role
nvm.sh Main shell script — defines the nvm function and all helpers
install.sh Installer — clones or downloads nvm, patches shell profiles
nvm-exec Executable wrapper: runs a command with NODE_VERSION in PATH
bash_completion Tab-completion definitions for nvm subcommands and versions
Makefile Test runner — delegates to urchin for fast/slow/install test suites
Dockerfile Container definition for CI test environments

Internal Functions (key subset)

Function Purpose
nvm_ls() Scans $NVM_DIR/versions/ for installed versions
nvm_ls_remote() Fetches and parses index.tab from mirror
nvm_install_binary() Downloads, checksums, and extracts Node binary tarball
nvm_install_node_source() Compiles Node from source
nvm_get_mirror() Returns the correct download mirror URL
nvm_get_checksum_binary() Detects available checksum tool (sha256sum, shasum, openssl, etc.)
nvm_compute_checksum() / nvm_compare_checksum() SHA-256 verification of downloaded artifacts
nvm_detect_profile() Finds the user's shell profile for loader injection
nvm_find_nvmrc() Walks parent directories to locate .nvmrc
nvm_process_nvmrc() Parses .nvmrc content (bare version or key=value pairs)
nvm_install_latest_npm() Upgrades npm to the highest compatible version for the active Node
nvm_sanitize_auth_header() Strips non-alphanumeric chars from NVM_AUTH_HEADER before use
nvm_get_arch() Detects CPU architecture including unofficial (Alpine musl, ARM variants)
nvm_get_os() Detects OS (darwin, linux, freebsd, openbsd, sunos, win)
nvm_die_on_prefix() Aborts if $PREFIX or $NPM_CONFIG_PREFIX would conflict

3. System Requirements

Runtime Dependencies

Dependency Required Notes
bash 3.2+ or zsh 5.0+ Yes Must be POSIX-compliant; sourced into the shell
curl or wget Yes (for install/download) Both supported; curl preferred
git For git install method Optional if using script method
awk Yes Used for index.tab parsing, version formatting
grep Yes Used throughout (with GREP_OPTIONS='' to prevent interference)
sed Yes Path/version string manipulation
sort Yes Version ordering
tar Yes (for binary installs) Extracts downloaded tarballs
sha256sum / shasum / openssl / bssl Yes Checksum verification of downloads
C compiler + make Source-only installs Not needed for binary installs

Platform Support

Platform Binary Source Notes
Linux (x86_64, arm64, armv7) Yes Yes Full support
macOS (x86_64, arm64) Yes Yes Requires Xcode CLT for source builds
Alpine Linux Unofficial binary Yes musl-based; unofficial binary slug
FreeBSD No Yes No official binary; source install only
OpenBSD No Yes No official binary; source install only
SunOS/SmartOS Version-dependent Yes Binary available for some versions only
Windows No No Not supported natively; use WSL

4. Configuration Reference

Environment Variables

Variable Default Required Description
NVM_DIR ~/.nvm (or $XDG_CONFIG_HOME/nvm) No nvm installation directory. Trailing slashes are stripped with a warning.
NVM_NODEJS_ORG_MIRROR https://nodejs.org/dist No Download mirror for Node.js binaries and index
NVM_IOJS_ORG_MIRROR https://iojs.org/dist No Download mirror for io.js (legacy)
NVM_SOURCE unset No Override source URL for install script
NVM_INSTALL_GITHUB_REPO nvm-sh/nvm No Override GitHub repo for installation. Changing this triggers a security warning in the installer.
NVM_INSTALL_VERSION latest tag No Pin the nvm version to install
NVM_AUTH_HEADER unset No Authorization header for authenticated mirror downloads. Non-alphanumeric chars are stripped before use.
NVM_NO_COLORS unset No Set to --no-colors to suppress ANSI color output globally
NVM_DEBUG unset No Set to 1 to enable debug logging in npm upgrade logic
NVM_INSTALL_THIRD_PARTY_HOOK unset No Path to a script called instead of the standard binary/source installer
NVM_CD_FLAGS '' (or -q on zsh) No Flags passed to cd internally; set automatically for zsh
METHOD auto No Override install method: git or script
NODE_VERSION unset No Used by install.sh to auto-install a Node version after nvm itself is installed
PROFILE auto-detected No Override shell profile file for installer. Set to /dev/null to prevent any profile modification.

Important

NVM_DIR must not have a trailing slash. nvm auto-corrects this and prints a warning, but scripts that set NVM_DIR should omit trailing slashes to avoid subtle path issues.

The .nvmrc File

A .nvmrc file in a project directory controls which Node.js version nvm selects when no version argument is given. nvm walks up the directory tree to find the nearest one.

Simple format:

v22.4.0

Key-value format (extended):

22.4.0
some-key=some-value

The key-value pairs are read by nvm_process_nvmrc() but currently only the bare version string is acted on by nvm commands. The key node is explicitly reserved and rejected.

Note

Comments in .nvmrc are supported: anything after # on a line is ignored. Leading/trailing whitespace is stripped. The file must contain exactly one bare version string (after stripping comments).

The default-packages File

$NVM_DIR/default-packages — one npm package name per line — lists packages to install globally whenever nvm install installs a new Node.js version.

# $NVM_DIR/default-packages
typescript
ts-node
prettier

Packages from npm link-ed directories in a source version are also re-linked by nvm reinstall-packages.

Alias Storage

Aliases are stored as plain text files in $NVM_DIR/alias/. The file name is the alias name; the file content is the target (a version string or another alias name). LTS aliases live in $NVM_DIR/alias/lts/.

Reserved (built-in) aliases that cannot be deleted:

  • node — resolves to latest stable
  • stable — resolves to latest stable
  • unstable — resolves to latest unstable (odd major)
  • iojs — resolves to latest io.js
  • system — refers to the system-installed Node.js outside nvm

Alias constraints:

  • Alias names may not contain / or #
  • Uppercase alias names work on case-insensitive filesystems (macOS HFS+) but fail on Linux (issue #3764)

5. Installation Details

Install Script Flow (install.sh)

nvm_do_install()
├── Check NVM_DIR validity
├── Detect macOS + Xcode requirement
├── Select method (git | script | auto)
│   ├── git: install_nvm_from_git()
│   │   ├── Clone or update git repo to $NVM_DIR
│   │   ├── Fetch and checkout tag $NVM_INSTALL_VERSION
│   │   └── gc --aggressive --prune=now
│   └── script: install_nvm_as_script()
│       ├── Download nvm.sh, nvm-exec, bash_completion in parallel
│       └── chmod +x nvm-exec
├── Detect shell profile (nvm_detect_profile())
├── Append loader + completion strings to profile
├── Source nvm.sh
├── nvm_check_global_modules() — warn about pre-existing globals
├── nvm_install_node() — install NODE_VERSION if set
└── nvm_reset() — unset all installer functions

Shell Profile Detection Order

nvm_detect_profile() checks in this order:

  1. $PROFILE environment variable (if set and points to an existing file)
  2. Shell-specific: bash → .bashrc, then .bash_profile; zsh → ${ZDOTDIR:-$HOME}/.zshrc, then .zprofile
  3. Fallback scan: .profile, .bashrc, .bash_profile, .zprofile, .zshrc

Set PROFILE=/dev/null to prevent any profile modification by the installer.

Removing nvm

No uninstaller exists. Remove manually:

rm -rf "$NVM_DIR"
# Edit ~/.bashrc / ~/.zshrc to remove the nvm loader lines

6. Operations

Binary Install Flow

nvm install <version>
└── nvm_install_binary(flavor, type, version, nosource)
    ├── Resolve version via mirror index
    ├── Build download URL: $MIRROR/$VERSION/node-$VERSION-$OS-$ARCH.$EXT
    ├── Download tarball (curl --compressed or wget fallback)
    ├── Verify checksum against SHASUMS256.txt
    ├── Extract to $NVM_DIR/versions/node/$VERSION/
    └── If successful: nvm use $VERSION

Supported compression: .tar.xz (preferred, requires xz), .tar.gz (fallback).

Source Build Flow

nvm install -s <version>
└── nvm_install_node_source(version, nosource, make_jobs, additional)
    ├── Download source tarball
    ├── Verify checksum
    ├── Extract to temp dir
    ├── ./configure --prefix=$NVM_DIR/versions/node/$VERSION
    ├── make -j<jobs>
    └── make install

Cache

Downloaded binaries are cached in $NVM_DIR/.cache/bin/ as node-<version>-<os>-<arch> files. nvm install --offline reads only from the cache without network access.

nvm cache dir      # print cache location
nvm cache clear    # wipe the cache

LTS Alias Refresh

When nvm ls-remote or nvm install --lts is called, nvm fetches index.tab and automatically updates $NVM_DIR/alias/lts/* and all named LTS alias files. These aliases are refreshed on every remote index fetch.

Checksum Verification

nvm detects available checksum binaries in this priority order: sha256sum → shasum → sha256 → gsha256sum → openssl → bssl → sha1sum → sha1

If only SHA-1 tools are available, nvm falls back to SHASUMS.txt (SHA-1). If no checksum binary is found, the download continues without verification and prints a warning.

Warning

On systems without any supported checksum binary, downloads are not verified. Ensure at least one of sha256sum, shasum, or openssl is installed.

nvm debug Output

nvm debug prints diagnostic information to stderr and exits with code 42:

nvm debug
nvm --version: v0.40.4
$SHELL: /bin/bash
$SHLVL: 1
${NVM_DIR}: '~/.nvm'
${PATH}: ~/.nvm/versions/node/v22.4.0/bin:...
curl: /usr/bin/curl, curl 8.x.x ...
git: /usr/bin/git, git version 2.x.x
nvm current: v22.4.0
which node: ~/.nvm/versions/node/v22.4.0/bin/node

Tip

Always include nvm debug output when reporting issues. It captures the complete environment state needed for diagnosis.

Updating nvm

Re-run the install script with the desired version:

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.4/install.sh | bash

The installer detects an existing $NVM_DIR/.git and runs git fetch + git checkout instead of cloning.


7. Security

Download Verification

All Node.js binaries are verified against SHASUMS256.txt (or SHASUMS.txt for SHA-1 fallback) fetched from the same mirror as the binary. If checksums don't match, the install fails with an error.

Caution

If NVM_NODEJS_ORG_MIRROR is set to a custom mirror, ensure that mirror also provides valid SHASUMS256.txt files. A mirror without checksum files will result in unverified downloads.

NVM_AUTH_HEADER Sanitization

nvm_sanitize_auth_header() strips all characters that are not alphanumeric, colon, semicolon, underscore, period, space, or hyphen before passing the header to curl/wget. This prevents header injection.

NVM_INSTALL_GITHUB_REPO Warning

If the installer is run with NVM_INSTALL_GITHUB_REPO set to anything other than nvm-sh/nvm, it prints a prominent security warning:

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE REPO IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

This mirrors the SSH host-key-change warning pattern and is intentional.

$PREFIX Conflict Detection

nvm_die_on_prefix() aborts nvm use if $PREFIX or $NPM_CONFIG_PREFIX is set to a path outside $NVM_DIR. This prevents npm from writing global packages to a system path that could conflict with nvm-managed installations.

No Root Required

nvm installs all Node.js versions into ~/.nvm/, which is owned by the user. No sudo is used or needed at any point. Global npm packages installed via nvm go into ~/.nvm/versions/node/<version>/lib/node_modules/.


8. Developer Internals

Repository Structure

nvm-sh/nvm/
├── nvm.sh                 — Main shell function library (~3,600 lines)
├── install.sh             — Self-contained installer
├── nvm-exec               — Executable: runs commands with NODE_VERSION in PATH
├── bash_completion        — Tab completion
├── Makefile               — Test suite runner
├── Dockerfile             — CI test image
├── README.md              — Project documentation
├── CONTRIBUTING.md        — Contribution guide
├── CHANGELOG.md           — Version history
├── ROADMAP.md             — Project direction
├── GOVERNANCE.md          — Governance model
├── test/
│   ├── common.sh          — Shared test helpers
│   ├── fast/              — Fast unit-style tests (no network, no install)
│   ├── slow/              — Integration tests (installs Node, requires network)
│   ├── install_script/    — Installer-specific tests
│   ├── installation_node/ — Full Node install+use tests
│   ├── installation_iojs/ — io.js install+use tests
│   ├── sourcing/          — Shell sourcing behavior tests
│   ├── xenial/            — Source-build tests on Ubuntu Xenial
│   ├── fixtures/          — Test fixture files including .nvmrc examples
│   └── mocks/             — OS detection mocks (uname, isainfo, etc.)
└── package.json           — Dev dependencies + test scripts

Code Patterns

All shell functions are prefixed nvm_ to avoid namespace collisions with user-defined functions. The top-level nvm() function itself dispatches to these helpers via a case statement.

GREP_OPTIONS='' command grep is used throughout to suppress user-defined grep aliases/options that could break pattern matching.

command prefix on external binaries prevents alias interference.

set -e / set -a / set -E compatibility: The nvm() function detects these shell options and temporarily disables them, running itself recursively in a clean environment. This allows nvm to work correctly regardless of the caller's set flags.

nvm_is_zsh guard: Multiple zsh-specific behaviors (glob expansion, word splitting) are handled with conditional setopt/unsetopt calls.

Version Directory Layout

$NVM_DIR/versions/node/v22.4.0/
├── bin/
│   ├── node
│   ├── npm -> ../lib/node_modules/npm/bin/npm-cli.js
│   └── npx -> ../lib/node_modules/npm/bin/npx-cli.js
├── include/node/
├── lib/
│   └── node_modules/
│       └── npm/
└── share/

The current symlink (created by nvm use unless NVM_SYMLINK_CURRENT=false) points to the active version directory.

npm Compatibility Matrix (from source)

nvm_install_latest_npm() implements a precise Node→npm compatibility matrix to avoid installing npm versions that are known-broken on specific Node releases:

Node range Maximum npm
v0.6.x npm@1.3
v0.9.x, v0.6.x No further upgrade
< v1.1.0 npm@4.5
< v4.0.0 npm@4
v4.x, v5.x (below 5.10) npm@5.3
v4.5–v4.6 npm@5.4.1
< v6.0.0 npm@5
v6.0–v6.1, v9.0–v9.2 npm@6.9
< v10.0.0 npm@6
v12.0–v12.12, v13.x, v15.x, v14.0–v14.14 npm@7
v12.13–v12.x, v14.13–v14.16, v16.0–v16.12, v17.x npm@8.6
< v18.17, v19.x, v20.0–v20.4 npm@9
< v20.17, v21.x, v22.0–v22.8 npm@10
≥ v20.17, ≥ v22.9 Latest npm

Testing

Tests are run via urchin (a POSIX test runner). Test files are plain shell scripts — each file is a test case; a zero exit code means pass.

# Run all tests for current shell
npm test

# Fast tests only (no network, no install)
npm run test/fast

# Slow integration tests (requires network)
npm run test/slow

# Installer tests
npm run test/install_script

Test suites are organized under test/fast/ and test/slow/, with each subdirectory representing a test group and each file in it a test case (executable bit required — enforced by npm run test:check-exec).

Key test categories:

  • Alias creation, resolution, circular alias detection
  • Version listing (local and remote, with mocks)
  • .nvmrc processing and sourcing behavior
  • Install/uninstall (slow, requires download)
  • nvm exec / nvm run / nvm use
  • Color output in pipelines
  • Security: wget injection prevention

Contributing

See CONTRIBUTING.md in the repository. Key points:

  • All new features require tests in test/fast/ or test/slow/
  • Shell scripts must pass shellcheck (enforced in CI)
  • Markdown files must pass markdown-link-check
  • .editorconfig enforces indentation and line endings (enforced by eclint)
  • Commits go directly to main; PRs are reviewed by maintainers per GOVERNANCE.md

9. Troubleshooting (Admin)

Error: Failed to clone nvm repo

Cause: Network connectivity issue or git not available.

Solution:

  1. Verify network: curl -I https://github.com
  2. Verify git: git --version
  3. Try the script method instead: METHOD=script curl -o- https://... | bash

Error: Checksums do not match

Cause: Download corruption or mirror serving stale/wrong files.

Solution:

  1. Clear the cache: nvm cache clear
  2. Retry: nvm install <version>
  3. If using a custom mirror, verify it serves correct SHASUMS256.txt.

Caution

Do not proceed with a failed checksum. The download is discarded automatically.


nvm use Breaks After set -u (Unbound Variable)

Cause: nvm references unset variables in nounset mode (issue #3820: PATTERN unbound variable).

Solution: Source nvm.sh before enabling set -u in your shell profile. This is an upstream known issue.


Global npm Packages Missing After nvm install

Cause: Global packages are version-scoped; each Node.js version has its own node_modules.

Solution:

nvm reinstall-packages <previous-version>
# or set up $NVM_DIR/default-packages before installing

$PREFIX Conflict Error

Cause: $PREFIX or $NPM_CONFIG_PREFIX is set, pointing outside $NVM_DIR.

Solution:

# Unset conflicting variables
unset PREFIX
unset NPM_CONFIG_PREFIX
nvm use <version>

Check ~/.npmrc and your shell profile for these settings.


Mirror Connectivity

Cause: Corporate firewall blocks nodejs.org or download mirrors.

Solution:

export NVM_NODEJS_ORG_MIRROR=https://your-internal-mirror.example.com/nodejs
export NVM_AUTH_HEADER="Bearer your-token-here"
nvm install 22