NVM
Modern software projects rarely depend on a single runtime. A legacy application may still require Node.js 14, while a newly created service might target Node.js 20 or 22. Installing one Node.js release globally can therefore create version conflicts, unexpected permission errors, and broken dependencies across unrelated projects.
NVM, short for Node Version Manager, solves this problem by allowing multiple Node.js versions to coexist on the same machine and by switching between them through the shell.
NVM is a shell-based version manager designed primarily for macOS and Linux environments. Instead of modifying system-wide installations, it stores each Node.js release in a separate directory, commonly under:
~/.nvm/versions/node/
Every installed version has its own bin, lib, and node_modules directories. This isolation means that global npm packages installed under one release do not automatically appear under another.
When you run a command such as:
nvm use 20.11.1
NVM updates the PATH variable for the current terminal session so that the selected version’s executables take priority. Commands including node, npm, and npx then resolve to that installation.
NVM is loaded as a shell function, usually through a startup file such as .bashrc, .zshrc, or a related profile script. Because the change affects the active shell rather than the entire operating system, different terminal windows can safely use different Node.js versions at the same time.
Basic NVM Usage
A typical workflow begins by installing a version:
nvm install 20.11.1
You can then activate it:
nvm use 20.11.1
To select the latest available release within a major version line:
nvm install 20
nvm use 20
Useful commands include:
nvm ls # List installed versions
nvm ls-remote # List versions available for download
nvm current # Show the active Node.js version
nvm alias default 20
nvm uninstall 18.20.4
The default alias determines which version NVM selects when no other version is explicitly requested. Setting a default is convenient for personal development, but automated environments should still activate a pinned version explicitly.
Project-Level Version Selection
Teams can record the required Node.js version in a .nvmrc file at the root of a repository:
20.11.1
After entering the project directory, NVM can read that file automatically:
nvm use
This removes the need to remember exact version numbers when moving between projects. A typical project setup might also include:
nvm install
nvm use
npm ci
Committing .nvmrc to version control helps local development, CI pipelines, and deployment systems agree on the same runtime. For greater flexibility, some teams use a major-version file such as 20 or lts/*, although an exact version provides the strongest reproducibility.
NVM Compared with Other Version Managers
Although NVM is the best-known option, it is not the only tool available to JavaScript developers.
| Tool | Strengths | Considerations |
|---|---|---|
| NVM | Mature ecosystem, extensive community support, flexible shell integration | Primarily targets POSIX systems; Windows users generally choose a separate implementation |
| Fnm | Fast startup, cross-platform support, compatibility with .nvmrc |
Requires installing a separate tool rather than relying on the traditional NVM shell script |
| Volta | Manages Node.js, package managers, and project-local CLI tools | Uses a different project configuration model and may require migration from existing workflows |
| nvm-windows | Native support for Windows and PowerShell | Uses different commands and behavior from the POSIX version of NVM |
NVM’s main advantage is its long-standing familiarity. Most Node.js documentation, tutorials, and troubleshooting guides assume a workflow involving nvm install and nvm use. Fnm offers modern performance and broader cross-platform consistency, while Volta focuses on making JavaScript tooling automatically follow project requirements.
For example, Volta can associate a Node.js version with a project and route package-manager commands to compatible versions. This can reduce configuration work, but teams already standardized around NVM may prefer not to introduce another workflow.
Use in CI/CD Pipelines
Consistency is especially important in continuous integration and deployment. A build that succeeds locally should not fail in CI because the pipeline uses a different Node.js release or package-manager version.
A CI job can install and activate the version recorded in .nvmrc:
version=$(cat .nvmrc)
nvm install "$version"
nvm use "$version"
npm ci
npm test
Many hosted CI platforms also provide dedicated actions or modules for installing Node.js. These tools often handle caching more efficiently than a manual NVM setup, but the underlying principle remains the same: select one clearly defined version and avoid relying on whatever version happens to be installed on the build agent.
A compatibility test matrix can cover several release lines:
strategy:
matrix:
node-version: [18, 20, 22]
This is useful for libraries and shared applications that need to support multiple runtime versions. Projects with a tightly controlled production environment may instead test only the exact deployed version.
Practical Recommendations
A few practices can make NVM more reliable:
- Commit
.nvmrcfiles. Treat the runtime version as part of the project configuration. - Use exact versions in automation. Exact pins prevent a newly published patch release from changing CI behavior unexpectedly.
- Keep package-manager lockfiles committed. Run
npm ci,pnpm install --frozen-lockfile, or the equivalent command in CI to reproduce dependency trees. - Avoid unnecessary global packages. Project-local dependencies are easier to isolate, reproduce, and upgrade.
- Rebuild native dependencies after switching. Packages containing native code may need recompilation for a different Node.js ABI.
- Do not confuse NVM with
nvm-windows. Their installation methods and command behavior are not identical. - Document setup expectations. A short onboarding section can prevent new contributors from spending time resolving avoidable version conflicts.
Conclusion
NVM provides a straightforward way to isolate Node.js installations and select the correct runtime for each project. Its shell-based switching model is fast, familiar, and particularly effective for developers maintaining several applications with different runtime requirements.
For teams standardized around NVM, a committed .nvmrc, deterministic dependency installation, and explicit CI version selection can eliminate most “wrong Node version” problems. Fnm and Volta are worthwhile alternatives when cross-platform performance or automatic management of JavaScript tooling is a higher priority. Regardless of the chosen tool, reliable version management is an essential part of a reproducible JavaScript development environment.