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
| Platform | Recommended Tool | Install Command |
|---|---|---|
| Linux | NVM | curl ... install.sh | bash |
| macOS | NVM or Homebrew | brew install nvm |
| Windows | nvm-windows or fnm | GitHub installer or winget install Schniz.fnm |
| Any (simple) | Official installer | Download from nodejs.org |
Essential NVM Commands
| Command | Purpose |
|---|---|
nvm install --lts | Install latest LTS version |
nvm install 20 | Install specific major version |
nvm use 18 | Switch to a version |
nvm alias default 20 | Set default for new shells |
nvm ls | List installed versions |
nvm ls-remote | List available versions |
nvm current | Show active version |
nvm uninstall 16 | Remove a version |
npm Configuration Commands
| Command | Purpose |
|---|---|
npm config set prefix ~/.npm-global | Set user-owned global directory |
npm config set cache ~/.npm-cache | Set custom cache directory |
npm config set registry https://registry.npmmirror.com | Set faster registry |
npm config list | Show current configuration |
npm config get prefix | Show current global prefix |
Verification Commands
| Command | Expected Result |
|---|---|
nvm --version | NVM version number |
node -v | Node.js version (e.g., v20.11.0) |
npm -v | npm version (e.g., 10.2.4) |
which node | Path 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
| Pitfall | Why It Happens | Fix |
|---|---|---|
nvm: command not found | Profile not sourced or shell not reloaded | source ~/.bashrc or restart terminal |
node: command not found after nvm use | NVM not initialized in shell | Check profile contains NVM initialization lines |
| Permission errors on global install | Using system Node.js, not NVM | Install via NVM; everything goes to ~/.nvm |
| Wrong Node version active | Default not set or not switched | nvm alias default 20; nvm use 20 |
node -v shows system version | NVM’s PATH comes after system PATH | Ensure NVM initialization is last in profile |
npm install -g still requires sudo | npm prefix points to system directory | npm 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
| Item | Value |
|---|---|
| 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.js | nvm install --lts |
| Switch version | nvm use 18 |
| Set default | nvm alias default 20 |
| List versions | nvm ls |
| Per-project pin | .nvmrc file with version number |
| Global package install | npm install -g pnpm (no sudo) |
| npm prefix (non-NVM) | npm config set prefix ~/.npm-global |
| Registry mirror | npm 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
sudois 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 --ltsinstalls the latest LTS version. This is the recommended choice for production applications.nvm alias defaultsets the version for new shells. Without it, each new terminal starts with no active Node.js version..nvmrcfiles enable per-project version switching. Runningnvm usein a directory with.nvmrcautomatically 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.comis 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!