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 --versionWindows
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-windowshas 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=0Troubleshooting
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.0Permission 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 ~/.nvmKeeping 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 versionnvm use <version>to activate it in the current sessionnvm alias default <version>to set a permanent default.nvmrcfiles 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.