Managing Multiple Node.js Versions with NVM

If you work on more than one JavaScript project, you have probably hit this problem: one project needs Node 16, another needs Node 20, and the system only has one version installed.

Reinstalling Node every time you switch projects is not a real solution. Node Version Manager (NVM) is. It lets you install multiple Node.js versions on the same machine and switch between them per project or per terminal session.

This post walks through installing NVM, the commands you will use daily, and how to lock versions per project so your whole team stays in sync.

Installing NVM

Before installing NVM, remove any existing system-wide Node.js installation. A leftover installation can cause PATH conflicts where commands resolve to the wrong binary.

macOS and Linux

Run the install script using curl:

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

Or with wget if you prefer:

wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash

The script adds initialization code to your shell profile automatically. After it finishes, reload your shell:

# For bash
source ~/.bashrc

# For zsh
source ~/.zshrc

Verify the installation worked:

nvm --version

Windows

NVM does not support Windows natively. A separate project called nvm-windows covers this. Download the installer from github.com/coreybutler/nvm-windows/releases, run it, and follow the wizard.

nvm-windows has slightly different command names from the macOS/Linux version. The core workflow is the same, but a few commands differ. This post focuses on macOS/Linux. Where Windows commands differ, they are noted.

Installing Node.js Versions

Once NVM is set up, installing a Node version is one command.

Install the current LTS release:

nvm install --lts

Install a specific version:

nvm install 18.17.0

Install the latest release (whatever is newest at the time):

nvm install node

NVM downloads the binary, stores it in ~/.nvm/versions/, and makes it active in the current terminal session.

To see every available version:

# macOS / Linux
nvm ls-remote

# Windows
nvm list available

The output is long. Filter it with grep:

nvm ls-remote | grep "v20"

Switching Versions

This is the main use case. Switch to a specific version:

nvm use 18.17.0

Switch to the latest LTS:

nvm use --lts

The change applies to the current terminal session only. Open a new terminal and you are back to whatever the default is.

Setting a Default Version

To avoid running nvm use every time you open a terminal, set a default:

nvm alias default 18.17.0

Every new session will start with Node 18.17.0 active.

Essential Commands

List all installed versions:

nvm ls

The output shows installed versions and marks the active one:

       v16.20.2
->     v18.17.0
       v20.10.0
default -> 18.17.0 (-> v18.17.0)

Check what is currently active:

nvm current

Run a command using a specific version without switching your session:

nvm exec 16.20.2 node app.js

This is useful for one-off checks or running scripts that need a specific version while you keep working on another.

Find the binary path for a version:

nvm which 18.17.0

Output:

/Users/username/.nvm/versions/node/v18.17.0/bin/node

Uninstall a version:

nvm uninstall 16.20.2

Uninstalling a version also removes all globally installed packages for that version. Make a note of what you have installed globally before removing a version.

Project-Specific Version Control with .nvmrc

The real productivity gain comes from locking Node versions per project. Create an .nvmrc file in your project root:

18.17.0

Or generate it from the command line:

echo "18.17.0" > .nvmrc

When you or a teammate clones the repo and runs nvm use from the project directory, NVM reads the file and switches to the correct version automatically. No version arguments needed.

This eliminates the "works on my machine" problem caused by different team members running different Node versions.

Auto-Switching on Directory Change

You can go one step further and have NVM switch versions automatically whenever you cd into a directory that has an .nvmrc. Add this to your ~/.zshrc:

autoload -U add-zsh-hook

load-nvmrc() {
  if [[ -f .nvmrc && -r .nvmrc ]]; then
    nvm use
  fi
}

add-zsh-hook chdir load-nvmrc

After reloading your shell, entering a directory with .nvmrc triggers nvm use without any manual step.

Managing Global Packages Across Versions

Each Node version has its own isolated set of global packages. If you install nodemon globally on Node 18, it will not exist on Node 20.

Install a global package for the current version:

npm install -g nodemon

When you upgrade to a new Node version, you can carry global packages over:

nvm install 20.10.0 --reinstall-packages-from=18.17.0

This installs Node 20.10.0 and copies all global packages from 18.17.0 to the new version.

View the global packages for your current version:

npm list -g --depth=0

Troubleshooting

nvm: command not found

The shell cannot locate NVM. The install script should have added initialization lines to your profile, but sometimes it does not. Check your ~/.bashrc, ~/.zshrc, or ~/.profile for these lines:

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"

If they are missing, add them manually and run source ~/.bashrc or source ~/.zshrc.

Slow Terminal Startup

NVM loads on every shell init, which adds a small delay. You can defer that load until you first use NVM by replacing the standard init lines with this lazy-loading version:

nvm() {
  unset -f nvm
  export NVM_DIR="$HOME/.nvm"
  [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
  nvm "$@"
}

NVM loads the first time you call it, not when the shell starts.

Version Not Persisting Across Terminals

You have not set a default. Fix it with:

nvm alias default 18.17.0

Permission Errors on Linux

Never run NVM commands with sudo. NVM is designed to run as your user, and using sudo breaks the permission model. If you are hitting permission errors, the likely cause is that ~/.nvm was created with wrong ownership. Fix it:

sudo chown -R $USER:$USER ~/.nvm

Keeping NVM Up to Date

NVM does not have an nvm update command. To upgrade, re-run the install script with the new version number in the URL. Check the NVM GitHub releases for the latest version, then run:

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

Replace v0.39.7 with the current release. The script is safe to re-run on an existing installation.

Wrapping Up

NVM reduces multi-version Node.js management to a handful of commands. The core workflow is:

  • nvm install <version> to get a version
  • nvm use <version> to activate it in the current session
  • nvm alias default <version> to set a permanent default
  • .nvmrc files to lock versions per project

One thing to keep in mind: global packages are version-specific. When you install a new Node version, reinstall or migrate your globals. It is a small cost for a development environment that stays predictable across projects and machines.