15%

Save 15% on All Hosting Services

Test your skills and get Discount on any hosting plan

Use code:

Skills
Get Started
08.10.2024

How to Install NVM for Node.js on Ubuntu: Complete Technical Guide

NVM (Node Version Manager) is a POSIX-compliant shell script that installs and manages multiple isolated Node.js runtime environments on a single machine, without requiring root privileges or modifying system-wide paths. Each Node.js version lives in its own directory under `~/.nvm/versions/node/`, giving you complete, conflict-free isolation between projects.

This guide walks through a production-grade NVM installation on Ubuntu (20.04, 22.04, and 24.04), covering not just the basic commands but also shell profile edge cases, `.nvmrc` workflow automation, global package migration, and server-specific pitfalls that most tutorials omit.

Why NVM Instead of the System Package Manager

Installing Node.js via `apt` places a single, system-wide binary at `/usr/bin/node`. Upgrading it affects every application on the host simultaneously. On a shared development machine or a VPS running multiple Node.js projects, this creates fragile, hard-to-reproduce environments.

NVM solves this by installing each Node.js version into a user-space directory and manipulating `PATH` at the shell level. The result is per-user, per-project version control with zero impact on the operating system's package state.

NVM vs. Other Node.js Version Managers

FeatureNVMfnmVoltan
LanguageShell (Bash/Zsh)RustRustShell
SpeedModerateVery fastVery fastFast
`.nvmrc` supportYesYesPartialNo
Per-project pinningYesYesYesNo
Windows supportNo (WSL only)YesYesNo
Global package isolationYesYesYesNo
Shell startup overhead~70ms~5ms~5msMinimal
Maturity / ecosystemHighestHighMediumHigh

NVM remains the most widely documented and ecosystem-supported option, making it the safest default for teams and server environments where reproducibility matters more than raw startup speed.

Prerequisites

  • Ubuntu 20.04, 22.04, or 24.04 (desktop or server)
  • A non-root user account with `sudo` privileges
  • `curl` or `wget` installed (both are present by default on most Ubuntu images)
  • Basic familiarity with Bash or Zsh

To confirm your shell and Ubuntu version before starting:

“`bash

echo $SHELL

lsb_release -a

“`

Step 1: Update the System Package Index

Refresh the APT package lists to ensure any dependencies resolved during the session are current:

“`bash

sudo apt-get update && sudo apt-get upgrade -y

“`

Also confirm that `curl` is available:

“`bash

curl –version || sudo apt-get install -y curl

“`

Step 2: Download and Run the NVM Installation Script

The official NVM installer is hosted on GitHub. It clones the NVM repository into `~/.nvm` and appends the necessary shell initialization block to your profile file.

Option A β€” Using curl (recommended):

“`bash

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

“`

Option B β€” Using wget:

“`bash

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

“`

Always verify the latest release tag on the NVM GitHub releases page before running. Replace `v0.40.1` with the current stable tag if a newer version has been published.

What the installer actually does:

  1. Clones the NVM repository to `~/.nvm`
  2. Detects your active shell (`bash`, `zsh`, `ksh`, or `fish`)
  3. Appends the following initialization block to the appropriate profile file (`~/.bashrc`, `~/.zshrc`, `~/.profile`, or `~/.bash_profile`)

“`bash

export NVM_DIR="$HOME/.nvm"

[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"

[ -s "$NVM_DIR/bash_completion" ] && . "$NVM_DIR/bash_completion"

“`

Security note: Piping a remote script directly into `bash` is a common pattern but carries inherent risk. For production servers or dedicated server environments, download the script first, inspect it, then execute:

“`bash

curl -o install_nvm.sh https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh

cat install_nvm.sh # review before running

bash install_nvm.sh

“`

Step 3: Activate NVM in the Current Shell Session

The installer modifies your profile, but those changes only take effect in new shell sessions. To activate NVM immediately without opening a new terminal:

For Bash:

“`bash

source ~/.bashrc

“`

For Zsh:

“`bash

source ~/.zshrc

“`

Manual sourcing (works in any shell):

“`bash

export NVM_DIR="$HOME/.nvm"

[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"

“`

Common Pitfall: Profile File Not Sourced in Non-Interactive Shells

On Ubuntu, `~/.bashrc` is only sourced for interactive non-login shells. If you are connecting via SSH (a login shell), Bash reads `~/.bash_profile` or `~/.profile` instead. If NVM is not found after SSH login, add the sourcing block to `~/.bash_profile`:

“`bash

echo 'source ~/.bashrc' >> ~/.bash_profile

source ~/.bash_profile

“`

This is one of the most frequently encountered issues when configuring NVM on remote servers.

Step 4: Verify the NVM Installation

“`bash

nvm –version

“`

Expected output (version number will vary):

“`

0.40.1

“`

If the command returns `nvm: command not found`, the shell profile was not sourced correctly. Re-run the sourcing command from Step 3 and verify that the initialization block exists in your profile file:

“`bash

grep -n 'NVM_DIR' ~/.bashrc

“`

Step 5: Install Node.js Using NVM

“`bash

nvm install –lts

“`

The `–lts` flag installs the most recent Long-Term Support release, which receives security patches for 30 months. For production workloads on a VPS with cPanel or any server environment, LTS is strongly preferred over the current release.

Install the Absolute Latest Release

“`bash

nvm install node

“`

Install a Specific Version

“`bash

nvm install 20.14.0

“`

List All Available Remote Versions

“`bash

nvm ls-remote

“`

To filter for LTS versions only:

“`bash

nvm ls-remote –lts

“`

Step 6: Verify the Active Node.js and npm Versions

“`bash

node -v

npm -v

“`

Also confirm the binary path to ensure you are not accidentally using a system-wide Node.js installation:

“`bash

which node

Expected: /home/<username>/.nvm/versions/node/v20.14.0/bin/node

“`

Step 7: Manage Multiple Node.js Versions

List All Locally Installed Versions

“`bash

nvm ls

“`

Sample output:

“`

-> v20.14.0

v18.20.3

v16.20.2

default -> lts/* (-> v20.14.0)

node -> stable (-> v20.14.0) (default)

lts/* -> lts/iron (-> v20.14.0)

“`

Switch Between Versions

“`bash

nvm use 18.20.3

“`

This change applies only to the current terminal session. Opening a new terminal reverts to the default alias.

Check Which Version Is Currently Active

“`bash

nvm current

“`

Step 8: Set a Persistent Default Node.js Version

To make a specific version the default for all new shell sessions:

“`bash

nvm alias default 20.14.0

“`

You can also alias to a release line rather than a specific patch version, which automatically tracks updates within that line:

“`bash

nvm alias default lts/*

“`

Step 9: Automate Version Switching with `.nvmrc`

This is one of NVM's most powerful and underused features. Place a `.nvmrc` file in your project root containing the required Node.js version:

“`bash

echo "20.14.0" > /path/to/your/project/.nvmrc

“`

Then, when you `cd` into that directory:

“`bash

nvm use

Found '/path/to/your/project/.nvmrc' with version <20.14.0>

Now using node v20.14.0

“`

Automatic Version Switching on Directory Change

Add the following to your `~/.bashrc` (or `~/.zshrc`) to trigger `nvm use` automatically whenever you enter a directory containing a `.nvmrc` file:

For Bash:

“`bash

cdnvm() {

command cd "$@" || return $?

nvm_path="$(nvm_find_up .nvmrc | command tr -d 'n')"

if [[ ! $nvm_path = *[^[:space:]]* ]]; then

declare default_version

default_version="$(nvm version default)"

if [[ $default_version == "N/A" ]]; then

nvm use default

elif [[ $(nvm current) != "$default_version" ]]; then

nvm use default

fi

elif [[ -r "$nvm_path/.nvmrc" && -r "$nvm_path" ]]; then

declare nvm_version

nvm_version=$(<"$nvm_path/.nvmrc")

declare locally_resolved_nvm_version

locally_resolved_nvm_version="$(nvm ls –no-colors "$nvm_version" | command tail -1 | command tr -d '->*' | command tr -d '[:space:]')"

if [[ "$locally_resolved_nvm_version" == "N/A" ]]; then

nvm install "$nvm_version"

elif [[ $(nvm current) != "$locally_resolved_nvm_version" ]]; then

nvm use "$nvm_version"

fi

fi

}

alias cd='cdnvm'

“`

This pattern is especially valuable in CI/CD pipelines and team environments where multiple developers work on projects with different runtime requirements.

Step 10: Manage Global npm Packages Across Versions

Each Node.js version installed by NVM has its own isolated `node_modules` directory for globally installed packages. This means tools like `pm2`, `yarn`, or `typescript` installed globally under `v18` are not available under `v20`.

Install a Global Package for the Active Version

“`bash

npm install -g yarn

npm install -g pm2

npm install -g typescript

“`

Migrate Global Packages When Installing a New Version

NVM provides a built-in flag to copy all global packages from one version to a new installation:

“`bash

nvm install 20.14.0 –reinstall-packages-from=18.20.3

“`

This is critical when upgrading Node.js versions on a server running persistent processes managed by PM2 or similar tools.

List Globally Installed Packages for the Current Version

“`bash

npm list -g –depth=0

“`

Step 11: Uninstall a Node.js Version

Before uninstalling, switch away from the version you want to remove:

“`bash

nvm use 20.14.0

nvm uninstall 16.20.2

“`

You cannot uninstall the currently active version. Attempting to do so returns an error.

Advanced: Using NVM in Non-Interactive Environments (CI/CD, Cron, Systemd)

NVM relies on shell initialization files that are not sourced in non-interactive shells. This causes `node: command not found` errors in cron jobs, systemd unit files, and some CI environments.

Solution 1: Use the full binary path

“`bash

/home/username/.nvm/versions/node/v20.14.0/bin/node /path/to/app.js

“`

Solution 2: Source NVM explicitly in scripts

“`bash

#!/bin/bash

export NVM_DIR="$HOME/.nvm"

[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"

nvm use 20.14.0

node /path/to/app.js

“`

Solution 3: Create a symlink for system-wide access (use with caution)

“`bash

sudo ln -s /home/username/.nvm/versions/node/v20.14.0/bin/node /usr/local/bin/node

“`

This approach sacrifices per-user isolation but is sometimes necessary for systemd services running as a dedicated service user.

NVM on Shared Hosting vs. VPS vs. Dedicated Servers

EnvironmentNVM SuitabilityNotes
Shared hostingNot supportedNo shell access; use platform-provided Node.js
[VPS Hosting](https://alexhost.com/vps/)ExcellentFull shell access; per-user isolation works perfectly
[Dedicated Servers](https://alexhost.com/dedicated-servers/)ExcellentIdeal for multi-project environments and CI runners
Docker containersPartialConsider using official Node.js Docker images instead
Shared Web HostingNot supportedNo SSH access in most configurations

For teams running Node.js applications alongside other services, a VPS gives you the shell-level control NVM requires without the overhead of managing physical hardware.

Practical Key-Takeaway Checklist

Use this as a deployment and configuration reference:

  • Verify shell type (`echo $SHELL`) before installing β€” Zsh and Bash require different profile files
  • Always use `–lts` for production Node.js installations; reserve `node` (latest) for experimental work
  • Commit `.nvmrc` to version control so every team member and CI runner uses the identical runtime version
  • Use `–reinstall-packages-from` when upgrading Node.js versions to avoid manually reinstalling global tools
  • Source NVM explicitly in any non-interactive script (cron, systemd, CI pipelines) β€” never assume the shell profile has been loaded
  • Audit global packages per version with `npm list -g –depth=0` after switching versions to catch missing dependencies early
  • Pin exact versions in `.nvmrc` (e.g., `20.14.0`) rather than aliases (e.g., `lts`) for maximum reproducibility in production
  • Check `which node` after switching versions to confirm you are not accidentally using a system-installed binary

Frequently Asked Questions

Does NVM require sudo or root access to install Node.js?

No. NVM installs everything under `~/.nvm` in the current user's home directory. No root privileges are needed for installing or switching Node.js versions. This is one of its primary advantages over system-level package managers.

Why does `nvm: command not found` appear after installation?

The NVM initialization block was added to your shell profile, but the profile has not been re-sourced in the current session. Run `source ~/.bashrc` (Bash) or `source ~/.zshrc` (Zsh). If the error persists after opening a new terminal, check that the initialization block was actually written to the correct file using `grep NVM_DIR ~/.bashrc`.

Can multiple users on the same server each have different Node.js versions via NVM?

Yes. Because NVM installs into each user's home directory (`~/.nvm`), every user maintains a completely independent set of Node.js versions and global packages. This is the correct architecture for multi-tenant servers.

What happens to globally installed npm packages when I switch Node.js versions with NVM?

Each Node.js version has its own isolated global package directory. Packages installed globally under one version are not visible to another. Use `nvm install <new-version> –reinstall-packages-from=<old-version>` to migrate them automatically.

Is NVM suitable for running Node.js applications in production on a server?

NVM is well-suited for managing which Node.js version is used, but for process management in production you should pair it with a process manager like PM2 or use systemd unit files. Ensure those non-interactive environments explicitly source NVM or reference the full binary path, as described in the CI/CD section above.

15%

Save 15% on All Hosting Services

Test your skills and get Discount on any hosting plan

Use code:

Skills
Get Started