Dev In The Mountain Header
A Developer In The mountains having fun

Why I Use nvm to Install Node and npm (and What the Modern Alternatives Are)

Whenever someone asks me how to install Node.js, my answer is almost always the same: don't install it from your OS package manager or the installer on nodejs.org — install it with nvm. nvm (Node Version Manager) installs Node and npm into your home directory and lets you switch between versions with one command.

There are newer, faster tools out there now — fnm, nvs, and Volta are the big ones — and they all have real advantages. I have tried them. I still use nvm. Here is why.


Why Not Just Install Node Directly?

Installing Node through apt, dnf, or the official installer works, but it causes problems sooner or later:

  • Old versions. Distro repos often ship a Node version that is a year or more behind the current LTS.
  • Global installs need sudo. When Node lives in a system directory, npm install -g either fails with permission errors or you end up running npm as root, which is a bad habit and a security risk.
  • One version only. Real life means working on a project pinned to Node 18, another on Node 22, and trying out the newest release on the side. A system install gives you exactly one version.
  • Painful upgrades and removals. Swapping versions with a system installer tends to leave stray files and broken global packages behind.

A version manager fixes all of these at once.


Why I Recommend nvm

It installs everything in your home directory

nvm puts each Node version under ~/.nvm/versions/node/. No sudo, no touching system files, and global npm packages install without permission errors. If you ever want to get rid of it, delete ~/.nvm and the few lines it added to your shell config.

Switching versions is trivial

bash
nvm install --lts        # install the latest LTS release
nvm install 22           # install the latest Node 22.x
nvm use 20               # switch this shell to Node 20
nvm alias default 22     # make 22 the default for new shells
nvm ls                   # list installed versions
nvm ls-remote --lts      # list every LTS release available

Per-project versions with .nvmrc

Drop a .nvmrc file in a project with the version in it:

bash
echo "22" > .nvmrc
nvm use        # reads .nvmrc and switches
nvm install    # installs the version from .nvmrc if you don't have it

Anyone else on the project who uses nvm (or fnm, or most other managers, since they also read .nvmrc) gets the same version.

Bring your global packages along

When a new Node release comes out, this installs it and moves your global packages over at the same time:

bash
nvm install 24 --reinstall-packages-from=22

It is the de facto standard

nvm has been around since 2010. Almost every tutorial, README, CI example, and Stack Overflow answer that mentions a Node version manager uses nvm. When something breaks, someone has already hit it and posted the fix. That counts for a lot.

Installing it

bash
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
# restart your shell, then:
nvm install --lts

Check the nvm releases page for the newest version number before you run that.


The Modern Alternatives

nvm is not perfect. Its biggest weaknesses:

  • Speed. nvm is a large POSIX shell script that gets sourced every time a shell starts. On some machines that adds a noticeable delay to opening a new terminal.
  • No native Windows support. nvm works on Linux, macOS, and WSL. On plain Windows you need the separate nvm-windows project, which works differently.
  • No automatic switching out of the box. cd-ing into a project with a .nvmrc does nothing unless you add a shell hook yourself.
  • Shell support. It works in bash and zsh, but fish users need a wrapper plugin.

The newer tools each go after one or more of those problems.

fnm (Fast Node Manager)

fnm is written in Rust and is the closest thing to a drop-in nvm replacement.

Advantages over nvm:

  • Fast. It is a single compiled binary, so shell startup cost is close to zero and switching versions is instant.
  • Cross-platform. Native builds for Linux, macOS, and Windows.
  • Automatic switching. With --use-on-cd, it switches versions when you cd into a directory with a .nvmrc or .node-version file.
  • Works with more shells. bash, zsh, fish, and PowerShell are all supported.
  • Familiar commands. fnm install, fnm use, fnm default map almost one-to-one onto nvm, and it reads the same .nvmrc files.
bash
curl -fsSL https://fnm.vercel.app/install | bash
# add to ~/.zshrc or ~/.bashrc:
eval "$(fnm env --use-on-cd)"

nvs (Node Version Switcher)

nvs is a cross-platform version switcher that is itself written in JavaScript and runs on Node.

Advantages over nvm:

  • Same tool on every OS. Windows, macOS, and Linux all use the same commands, which is nice if you move between systems a lot.
  • Interactive menus. Running nvs by itself gives you a menu to pick a version instead of typing one.
  • Multiple remotes. It can pull builds from more than just the official Node mirror, such as nightly builds or other forks.
  • Auto-switching based on .node-version / .nvmrc with nvs auto on.
  • VS Code integration. It can tell VS Code which Node version to use for debugging.

The trade-off is that it has a much smaller community than nvm or fnm and does not get updated very often.

Volta

Volta takes a different approach. Instead of you switching versions, it puts shims on your PATH that pick the right version for whatever project you are in.

Advantages over nvm:

  • Fully automatic. You never run a "use" command. Volta reads the project's package.json and runs the right Node every time.
  • Pins the whole toolchain. volta pin node@22 npm@10 writes the versions into package.json, and it can pin yarn and pnpm too, so everyone on the team gets the exact same toolchain.
  • Global tools stay put. A CLI installed with volta install keeps working with the Node version it was installed with, even when you switch projects.
  • Fast and cross-platform. Written in Rust, with native Windows support.

One big caveat: the Volta maintainers have announced that the project is no longer actively maintained, and they point users toward mise instead. It still works, but I would not move a team onto it today.

A Quick Word on mise

Since it keeps coming up: mise (formerly rtx) is a version manager for everything — Node, Python, Go, Rust, and a lot more — in one Rust binary. It reads .nvmrc, .node-version, and its own mise.toml. If you juggle many languages and want a single tool for all of them, it is worth a look.


Side-by-Side

nvmfnmnvsVolta
Written inShell scriptRustJavaScriptRust
SpeedSlowestVery fastFastVery fast
Native WindowsNo (nvm-windows)YesYesYes
Auto-switch on cdManual hookBuilt inBuilt inBuilt in (shims)
Reads .nvmrcYesYesYesNo (package.json)
Pins npm/yarn/pnpmNoNoNoYes
Community sizeHugeLargeSmallLarge, but unmaintained

So Why Do I Still Use nvm?

With all that said, nvm is still what is on my machines, and it is still what I tell people to install first.

  • I am on Linux and WSL. nvm's main weakness, Windows support, does not affect me.
  • It is everywhere. Docs, CI configs, Dockerfiles, and coworkers' setups all assume nvm. When I copy a command from a README, it just works.
  • Easiest to support. When I help someone set up Node, nvm has the most answers online when something goes wrong. That matters more to a beginner than shaving a few milliseconds off shell startup.
  • It is plain shell. No binary to download per platform and nothing to compile. It runs anywhere bash or zsh runs, including bare-bones servers.
  • The slow startup is fixable. Lazy-loading nvm (for example, with the lazy option in oh-my-zsh's nvm plugin, or a small wrapper function) makes startup cost basically go away.
  • Auto-switching is a few lines away. A small zsh hook that runs nvm use when a .nvmrc is found covers most of what fnm gives you for free.
  • I don't switch versions that often. For how I work, the speed difference between nvm use and fnm use doesn't matter.
  • It is stable. It has been around for over 15 years and isn't going anywhere, which is more than I can say for Volta at this point.

My recommendation

  • Most people, especially on Linux, macOS, or WSL: use nvm. It is the safe, well-documented default.
  • You want speed, or you are on native Windows: use fnm. It is the best drop-in upgrade, and your .nvmrc files keep working.
  • You want one tool for Node, Python, Go, and more: look at mise.
  • You want strict per-project toolchain pinning: Volta's model is great, but since it is no longer maintained, mise is the better choice for that now too.

Whatever you choose, just don't sudo npm install -g on a system-installed Node. Your future self will thank you.

If you want more terminal tools, check out my CLI tools list and Why I use so many CLI/TUI tools.

More places to find me
Mental Health
follow me on Mastodon