| |

Node.js 2 🟢 Installing Node.js, NVM, and Setting Up the Environment

Node.js installation is straightforward, but the choice of tooling matters more than the installation itself. Installing Node.js directly from the official website or a distribution package manager works for simple cases, but it creates a single, system-wide version that is difficult to change. Modern Node.js development almost always involves multiple projects that require different Node.js versions, and switching between them with a system installation means uninstalling and reinstalling each time.

This chapter covers the recommended installation approach for each major platform, with emphasis on Node Version Manager (NVM) as the standard tool for managing multiple Node.js versions on a single machine. We will walk through installing NVM on Linux and macOS, the Windows alternatives, verifying the installation, and configuring the environment for practical development. By the end, you will have a working Node.js environment and the tooling to switch versions per project.

Key point: Use a Node version manager (NVM on Linux/macOS, nvm-windows or fnm on Windows) instead of a system installer. Version managers allow multiple Node.js versions to coexist and switch per project without permission errors.


Why NVM exists

The multi-version problem. Real-world Node.js projects pin different Node.js versions. A legacy application might require Node 14, a modern API might use Node 20, and a new project might target Node 22. A system-wide installation can only hold one version at a time. Switching requires uninstalling and reinstalling, which is slow and disruptive. NVM solves this by installing each Node.js version into its own directory under the user’s home folder and switching between them by modifying the shell’s PATH.

The permission problem. The official Node.js installer places global packages in a system directory like /usr/local/lib/node_modules, which requires sudo for global installs. Using sudo npm install -g creates files owned by root, leading to permission errors later when running the package as a regular user. NVM installs everything into the user’s home directory (~/.nvm), eliminating the need for sudo and preventing permission issues entirely.

The isolation problem. When multiple projects run on the same machine, global packages installed for one project can interfere with another. NVM’s per-version isolation means that each Node.js version has its own global package directory. A global tool installed under Node 18 does not appear under Node 20. This isolation prevents version conflicts and makes project environments reproducible.

The reproducibility problem. A project that specifies Node 20 in its documentation but is developed on Node 22 can encounter subtle differences in behavior, deprecation warnings, or library compatibility. By pinning the version in a .nvmrc file and using nvm use to switch automatically, teams ensure that every developer and CI runner executes the code under the same runtime. This eliminates an entire category of “works on my machine” failures.

The CI and container parity problem. Continuous integration systems and Docker images both need to select a Node.js version. NVM’s .nvmrc convention is understood by CI providers like GitHub Actions through actions such as actions/setup-node, and the official Node.js Docker images provide version-specific tags. Aligning local, CI, and container versions through the same version specifier removes discrepancies that otherwise surface only in production.


a. Installing NVM on Linux and macOS

NVM is a bash script that manages Node.js installations. It does not support Windows natively; Windows users should use nvm-windows or fnm instead.

The installation uses a single curl or wget command:

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

Or with wget:

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

The installation script clones the NVM repository to ~/.nvm and adds initialization lines to your shell profile (~/.bashrc, ~/.zshrc, or ~/.profile). After installation, reload your shell or source the profile:

source ~/.bashrc

Verify that NVM is available:

nvm --version

If the command is not found, the shell profile was not updated or not sourced. Check that the following lines exist in your profile and source it again:

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

The initialization lines must appear after any earlier PATH modifications so that NVM’s directory takes precedence over a system Node.js installation. If both exist and the system version is being used, the initialization order in the profile is the usual cause.

b. Installing Node.js with NVM

Once NVM is installed, installing Node.js is a single command. The --lts flag installs the latest Long Term Support version, which is the recommended choice for production applications:

nvm install --lts

To install a specific version:

nvm install 20.11.0

After installation, NVM automatically sets the newly installed version as the active one. You can verify with:

node -v
npm -v

To see all installed versions and which one is currently active:

nvm ls

To switch to a different installed version:

nvm use 18

To set a default version that activates in every new shell:

nvm alias default 20

The default alias is what makes new terminal sessions usable without an explicit nvm use call. Without it, a fresh shell has no Node.js on its PATH until you switch manually.

c. Windows alternatives: nvm-windows and fnm

NVM does not run on Windows. Two alternatives provide similar functionality. nvm-windows is a separate project with a similar command-line interface. Download the installer from the nvm-windows GitHub releases page, run it, and then use commands like nvm install lts and nvm use 20.11.0.

A newer alternative is fnm (Fast Node Manager), which is cross-platform and written in Rust. It offers faster startup and simpler shell integration. On Windows, fnm can be installed via winget or a standalone installer:

winget install Schniz.fnm

After installation, configure your shell and install Node:

fnm install 22
fnm use 22

For a simpler Windows setup without a version manager, the official Node.js installer or winget can install a single LTS version:

winget install OpenJS.NodeJS.LTS

This works for simple projects but lacks the multi-version flexibility of NVM. On Windows, nvm-windows requires administrator privileges for the initial installation because it creates a symbolic link at C:\Program Files\nodejs that points to the active version. After installation, nvm use requires an elevated shell unless the link target is user-writable.

d. Configuring npm global paths and caches

After installing Node.js via NVM, global npm packages are stored in the NVM directory alongside the Node.js version that installed them. No additional configuration is required; npm install -g works without sudo.

If you are using a system installation instead of NVM and encounter permission errors on global installs, configure npm to use a user-owned directory rather than sudo:

mkdir -p ~/.npm-global
npm config set prefix '~/.npm-global'
echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.bashrc
source ~/.bashrc

To configure a custom cache directory, which can be useful for CI environments or disk space management:

npm config set cache ~/.npm-cache

For faster downloads in regions where the default registry is slow, set a mirror:

npm config set registry https://registry.npmmirror.com

These settings are stored in ~/.npmrc and apply to all projects for the current user. The npm prefix determines where global packages are installed and where the node_modules tree for global tools lives. Changing it after installing global packages can orphan the previous installations, so set it before installing anything globally.


Complete Example Session

# ============================================
# PART 1: INSTALL NVM
# ============================================
# The official installation script.

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
# ============================================
# PART 2: RELOAD SHELL
# ============================================
# Make the nvm command available.

source ~/.bashrc
nvm --version
# ============================================
# PART 3: INSTALL LATEST LTS NODE.JS
# ============================================
# --lts installs the Long Term Support version.

nvm install --lts
# ============================================
# PART 4: VERIFY INSTALLATION
# ============================================
# Check that node and npm are available.

node -v
npm -v
# ============================================
# PART 5: INSTALL A SPECIFIC VERSION
# ============================================
# Install Node.js 18 alongside the LTS version.

nvm install 18
# ============================================
# PART 6: LIST INSTALLED VERSIONS
# ============================================
# See all versions and the active one.

nvm ls
# ============================================
# PART 7: SWITCH VERSIONS
# ============================================
# Change the active Node.js version.

nvm use 18
node -v
# ============================================
# PART 8: SET DEFAULT VERSION
# ============================================
# New shells will use this version by default.

nvm alias default 20
# ============================================
# PART 9: INSTALL A GLOBAL PACKAGE
# ============================================
# No sudo needed with NVM.

npm install -g pnpm
pnpm -v
# ============================================
# PART 10: CONFIGURE REGISTRY MIRROR
# ============================================
# Optional: use a faster registry for downloads.

npm config set registry https://registry.npmmirror.com
npm config get registry

These ten parts cover the full NVM workflow: installation, shell integration, installing multiple Node.js versions, switching between them, setting defaults, installing global packages without permissions issues, and optionally configuring a faster registry.


Quick Reference

Platform Installation Methods

PlatformRecommended ToolInstall Command
LinuxNVMcurl ... install.sh | bash
macOSNVM or Homebrewbrew install nvm
Windowsnvm-windows or fnmGitHub installer or winget install Schniz.fnm
Any (simple)Official installerDownload from nodejs.org

Essential NVM Commands

CommandPurpose
nvm install --ltsInstall latest LTS version
nvm install 20Install specific major version
nvm use 18Switch to a version
nvm alias default 20Set default for new shells
nvm lsList installed versions
nvm ls-remoteList available versions
nvm currentShow active version
nvm uninstall 16Remove a version

npm Configuration Commands

CommandPurpose
npm config set prefix ~/.npm-globalSet user-owned global directory
npm config set cache ~/.npm-cacheSet custom cache directory
npm config set registry https://registry.npmmirror.comSet faster registry
npm config listShow current configuration
npm config get prefixShow current global prefix

Verification Commands

CommandExpected Result
nvm --versionNVM version number
node -vNode.js version (e.g., v20.11.0)
npm -vnpm version (e.g., 10.2.4)
which nodePath under ~/.nvm/

Best Practices

✅ Do This:

nvm install --lts                              # Install LTS for production work
nvm alias default 20                           # Set a default version
nvm use 18                                     # Switch per project as needed
echo "20" > .nvmrc                             # Pin project version
npm config set prefix ~/.npm-global           # User-owned global dir (non-NVM)
npm install -g pnpm                           # Global installs without sudo

❌ Don’t Do This:

sudo npm install -g package                    # ❌ Permission chaos
apt install nodejs                             # ❌ System Node.js; outdated, hard to change
nvm use 20                                     # ❌ Before installing version 20
npm config set registry ...                    # ❌ Without verifying it responds

Common Pitfalls

PitfallWhy It HappensFix
nvm: command not foundProfile not sourced or shell not reloadedsource ~/.bashrc or restart terminal
node: command not found after nvm useNVM not initialized in shellCheck profile contains NVM initialization lines
Permission errors on global installUsing system Node.js, not NVMInstall via NVM; everything goes to ~/.nvm
Wrong Node version activeDefault not set or not switchednvm alias default 20; nvm use 20
node -v shows system versionNVM’s PATH comes after system PATHEnsure NVM initialization is last in profile
npm install -g still requires sudonpm prefix points to system directorynpm config set prefix ~/.npm-global

Real-World Examples

1. Project-Specific Node Version

echo "20" > .nvmrc
nvm use  # reads .nvmrc and switches

2. CI Install with GitHub Actions

- uses: actions/setup-node@v4
  with:
    node-version-file: '.nvmrc'

3. Docker Node.js Image

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

4. Verify Node.js Installation

node -e "console.log(process.version)"

5. Switch for Testing

nvm use 18 && npm test
nvm use 20 && npm test

6. Install pnpm Globally

npm install -g pnpm
pnpm --version

7. Set Faster Registry for pnpm

pnpm config set registry https://registry.npmmirror.com

8. List All Remote LTS Versions

nvm ls-remote --lts

9. Uninstall a Node Version

nvm uninstall 16

10. Check npm Configuration

npm config list

Visual

NVM Architecture

┌──────────────────────────────────────────────────────────────┐
│  NVM MANAGES MULTIPLE NODE.JS VERSIONS                       │
│                                                              │
│  ~/.nvm/                                                     │
│  ├── nvm.sh                  ← NVM script                    │
│  ├── alias/                  ← default version aliases       │
│  └── versions/                                               │
│      └── node/                                               │
│          ├── v18.20.0/       ← Node 18 installation          │
│          │   ├── bin/                                        │
│          │   │   ├── node                                    │
│          │   │   └── npm                                     │
│          │   └── lib/node_modules/  ← Global packages        │
│          │                                                   │
│          ├── v20.11.0/       ← Node 20 installation          │
│          │   ├── bin/                                        │
│          │   └── lib/node_modules/  ← Separate globals       │
│          │                                                   │
│          └── v22.0.0/        ← Node 22 installation          │
│              └── ...                                         │
│                                                              │
│  nvm use 18  → PATH points to v18.20.0/bin                   │
│  nvm use 20  → PATH points to v20.11.0/bin                   │
│  nvm alias default 20  → new shells use v20                  │
└──────────────────────────────────────────────────────────────┘

Installation Decision Flow

┌──────────────────────────────────────────────────────────────┐
│  CHOOSING AN INSTALLATION METHOD                             │
│                                                              │
│  What is your platform?                                      │
│       │                                                      │
│       ├── Linux/macOS ──▶ NVM                                │
│       │                   curl install.sh | bash             │
│       │                   nvm install --lts                  │
│       │                                                      │
│       ├── Windows ──▶ nvm-windows or fnm                     │
│       │               GitHub installer or winget             │
│       │                                                      │
│       └── Docker/CI ──▶ Official node image                  │
│                         FROM node:20-alpine                  │
│                                                              │
│  Do you need multiple versions?                              │
│       │                                                      │
│       ├── Yes ──▶ Version manager (NVM/fnm)                  │
│       │                                                      │
│       └── No ──▶ Official installer or winget/brew           │
│                  (simpler, but less flexible)                │
└──────────────────────────────────────────────────────────────┘

PATH Resolution with NVM

┌──────────────────────────────────────────────────────────────┐
│  HOW NVM CHANGES THE PATH                                    │
│                                                              │
│  After `nvm use 18`:                                         │
│  PATH=~/.nvm/versions/node/v18.20.0/bin:$PATH                │
│       │                                                      │
│       └── node, npm, npx resolve here first                  │
│                                                              │
│  After `nvm use 20`:                                         │
│  PATH=~/.nvm/versions/node/v20.11.0/bin:$PATH                │
│       │                                                      │
│       └── node, npm, npx resolve to v20 now                  │
│                                                              │
│  The system Node.js (if installed) remains in PATH but       │
│  after the NVM directory, so it is not used.                 │
│                                                              │
│  To verify: `which node` should show ~/.nvm/.../bin/node     │
└──────────────────────────────────────────────────────────────┘

Per-Project Version with .nvmrc

┌──────────────────────────────────────────────────────────────┐
│  .nvmrc ENABLES AUTOMATIC PER-PROJECT SWITCHING              │
│                                                              │
│  project-a/                                                  │
│  ├── .nvmrc  → "18"                                          │
│  └── src/                                                    │
│                                                              │
│  project-b/                                                  │
│  ├── .nvmrc  → "20"                                          │
│  └── src/                                                    │
│                                                              │
│  $ cd project-a && nvm use                                   │
│  Found '.nvmrc' with version <18>                            │
│  Now using node v18.20.0                                     │
│                                                              │
│  $ cd project-b && nvm use                                   │
│  Found '.nvmrc' with version <20>                            │
│  Now using node v20.11.0                                     │
│                                                              │
│  Add shell hooks to run `nvm use` automatically on cd.       │
└──────────────────────────────────────────────────────────────┘

Summary

ItemValue
Recommended tool (Linux/macOS)NVM (Node Version Manager)
Recommended tool (Windows)nvm-windows or fnm
Install command (Linux/macOS)curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
Install Node.jsnvm install --lts
Switch versionnvm use 18
Set defaultnvm alias default 20
List versionsnvm ls
Per-project pin.nvmrc file with version number
Global package installnpm install -g pnpm (no sudo)
npm prefix (non-NVM)npm config set prefix ~/.npm-global
Registry mirrornpm config set registry https://registry.npmmirror.com

Key takeaways:

  • Use a version manager, not a system installer. NVM on Linux and macOS allows multiple Node.js versions to coexist and switch per project.
  • NVM installs everything in the user’s home directory. No sudo is needed for global package installs, eliminating permission errors.
  • Windows requires an alternative. NVM does not support Windows; use nvm-windows or fnm instead.
  • nvm install --lts installs the latest LTS version. This is the recommended choice for production applications.
  • nvm alias default sets the version for new shells. Without it, each new terminal starts with no active Node.js version.
  • .nvmrc files enable per-project version switching. Running nvm use in a directory with .nvmrc automatically switches to the specified version.
  • For non-NVM installations, configure a user-owned npm prefix. This prevents permission errors on global installs.
  • A registry mirror speeds up downloads in some regions. npm config set registry https://registry.npmmirror.com is a common configuration.

Remember: The choice of how to install Node.js determines how painful version management will be later. A system installation works until you need a second version, then becomes a source of friction. NVM on Linux and macOS, or nvm-windows and fnm on Windows, solves this by installing each Node.js version into an isolated directory and switching between them by modifying the shell’s PATH. The installation itself is one command, and the workflow of installing, switching, and setting defaults becomes second nature after a few uses. Configure a user-owned npm prefix if you ever use a non-NVM installation, and set a faster registry if you are in a region where the default npm registry is slow. With the environment set up correctly, you can focus on writing JavaScript rather than fighting your tools.



Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!