# nvm — Administrator & Developer Guide

**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.

```text
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:**
```text
v22.4.0
```

**Key-value format (extended):**
```text
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.

```text
# $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:

```bash
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.

```bash
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:

```bash
nvm debug
```

```text
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:

```bash
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:

```text
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    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.

```bash
# 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:**
```bash
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:**
```bash
# 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:**
```bash
export NVM_NODEJS_ORG_MIRROR=https://your-internal-mirror.example.com/nodejs
export NVM_AUTH_HEADER="Bearer your-token-here"
nvm install 22
```

---

_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. Unofficial; not affiliated with or reviewed by the project._
