cheat sheet
Node.js Installation
Install Node.js on Windows, macOS, and Linux using winget, Homebrew, apt, nvm, Volta, or fnm. Covers version managers, LTS vs current, and verifying your install.
Node.js Installation
What it is
Node.js is a cross-platform JavaScript runtime built on Chrome's V8 engine. Installing it gives you the node binary for running scripts and the npm package manager. Most developers use a version manager (nvm, Volta, or fnm) rather than a system-level install so they can switch Node versions per project.
Install
Windows — winget
winget install --id OpenJS.NodeJS.LTS
Output:
Found Node.js (LTS) [OpenJS.NodeJS.LTS] Version 22.14.0
This application is licensed to you by its owner.
Microsoft is not responsible for, nor does it license, third-party packages.
Downloading https://nodejs.org/dist/v22.14.0/node-v22.14.0-x64.msi
Successfully installed
macOS — Homebrew
brew install node
Output:
==> Downloading https://ghcr.io/v2/homebrew/core/node/manifests/22.14.0
==> Installing node
==> Summary
🍺 /usr/local/Cellar/node/22.14.0: 2,412 files, 71.5MB
Ubuntu / Debian — apt (via NodeSource)
# Add the NodeSource v22 repository
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt-get install -y nodejs
Output:
Selecting previously unselected package nodejs.
Setting up nodejs (22.14.0-1nodesource1) ...
nvm — Node Version Manager (recommended cross-platform approach)
nvm is the most widely used approach for managing multiple Node versions. It installs Node entirely in your home directory — no sudo required.
Install nvm
# macOS / Linux
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
Restart your shell (or source ~/.bashrc / source ~/.zshrc), then verify:
nvm --version
Output:
0.39.7
Install and use a Node version
# Install the latest LTS release
nvm install --lts
Output:
Downloading and installing node v22.14.0...
Downloading https://nodejs.org/dist/v22.14.0/node-v22.14.0-linux-x64.tar.xz...
Now using node v22.14.0 (npm v10.9.2)
Creating default alias: default -> lts/* (-> v22.14.0)
# Install a specific version
nvm install 20.18.0
# Switch to a version
nvm use 20.18.0
# Switch back to latest LTS
nvm use --lts
# Set the default version for new shells
nvm alias default 22
# List installed versions
nvm ls
Output (nvm ls):
-> v22.14.0
v20.18.0
default -> 22 (-> v22.14.0)
lts/* -> lts/jod (-> v22.14.0)
.nvmrc — pin a version per project
# Create a .nvmrc file in your project root
echo "22" > .nvmrc
# Any developer with nvm can then run:
nvm use
# nvm reads .nvmrc and switches automatically
Output:
Found '/home/user/myproject/.nvmrc' with version <22>
Now using node v22.14.0 (npm v10.9.2)
Volta — toolchain manager
Volta pins Node versions per project in package.json and automatically switches when you cd into a directory. It works on Windows without WSL.
# Install Volta (macOS / Linux)
curl https://get.volta.sh | bash
# Install Volta (Windows — run in PowerShell as admin)
winget install Volta.Volta
# Install Node LTS and make it the default
volta install node
# Install a specific version
volta install node@20
# Pin a version to the current project (writes to package.json)
volta pin node@22
Output (volta install node):
success: installed and set node@22.14.0 (with npm@10.9.2) as default
Output (volta pin node@22):
success: pinned node@22.14.0 (with npm@10.9.2) in package.json
The package.json entry Volta adds:
{
"volta": {
"node": "22.14.0",
"npm": "10.9.2"
}
}
fnm — Fast Node Manager
fnm is written in Rust and is significantly faster than nvm at shell startup time. It also reads .nvmrc and .node-version files.
# macOS
brew install fnm
# Linux (curl installer)
curl -fsSL https://fnm.vercel.app/install | bash
# Windows (winget)
winget install Schniz.fnm
Add the shell hook (add to ~/.bashrc, ~/.zshrc, or ~/.config/fish/config.fish):
# bash / zsh
eval "$(fnm env --use-on-cd)"
# fish
fnm env --use-on-cd | source
# Install and use the latest LTS
fnm install --lts
fnm use lts-latest
# Install a specific version
fnm install 20
fnm use 20
# List installed versions
fnm list
Output (fnm install --lts):
Installing Node v22.14.0 (x64)
Output (fnm list):
* v22.14.0 default (lts-latest)
v20.18.0
Verify the installation
node --version
Output:
v22.14.0
npm --version
Output:
10.9.2
LTS vs Current — when to use each
| Channel | Example | Stability | Who should use it |
|---|---|---|---|
| LTS (Long-Term Support) | v22.x (Active), v20.x (Maintenance) | High — bug + security fixes for 3 years | Production apps, team projects, CI |
| Current | v23.x | New features land here first; breaks possible | Trying new Node APIs, library authors |
Use LTS for anything that runs in production. LTS versions have even major version numbers (18, 20, 22). Odd numbers (19, 21, 23) are Current releases and have a shorter support window.
Version manager comparison
| Feature | nvm | Volta | fnm |
|---|---|---|---|
| Shell startup speed | Slow (bash function) | Fast (binary shim) | Fast (Rust binary) |
| Windows support | WSL only | Yes (native) | Yes (native) |
| Per-project pin | .nvmrc | package.json volta field | .nvmrc / .node-version |
Reads .nvmrc | Yes | Yes | Yes |
| Also manages npm/yarn/pnpm | No | Yes | No |
| Install without root | Yes | Yes | Yes |
| Ecosystem maturity | Very mature | Mature | Growing |
Pick nvm if you're on macOS/Linux and want maximum compatibility. Pick Volta if you're on Windows or want
package.json-level pinning shared with the whole team. Pick fnm if shell startup time matters (e.g., you open many terminals).
n — minimal version manager
n is the smallest of the Node version managers — a single bash script with no shell hooks. It installs Node directly into the system Node prefix (default /usr/local), which is the cleanest layout but requires sudo unless you re-point the prefix. Reach for n when you want zero magic and no shell-startup overhead, on a machine where you're the only Node user.
# Install n itself (one-off bootstrap with curl)
curl -L https://raw.githubusercontent.com/tj/n/master/bin/n -o /usr/local/bin/n
chmod +x /usr/local/bin/n
# Or via npm if you already have Node
npm install -g n
Output: (none — exits 0 on success)
# Install the latest LTS
n lts
# Install a specific version
n 22.14.0
# Install latest current release
n latest
# Switch interactively (arrow keys)
n
# Remove a version
n rm 20.18.0
Output (n lts):
installing : node-v22.14.0
mkdir : /usr/local/n/versions/node/22.14.0
fetch : https://nodejs.org/dist/v22.14.0/node-v22.14.0-linux-x64.tar.xz
installed : v22.14.0 (with npm 10.9.2)
To avoid sudo, re-point the prefix at a user-owned directory:
# Add to ~/.bashrc or ~/.zshrc
export N_PREFIX="$HOME/.n"
export PATH="$N_PREFIX/bin:$PATH"
echo $N_PREFIX
Output:
/home/alice/.n
asdf — multi-language version manager
asdf manages versions for many runtimes (Node, Python, Ruby, Go, Terraform…) through a plugin system. One tool, one config file (.tool-versions), one PATH manipulation. Reach for asdf when you switch between multiple languages and want one cohesive setup; the trade-off is slower shell startup than fnm/mise.
# Install asdf via git
git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.14.0
# Add to ~/.bashrc / ~/.zshrc
. "$HOME/.asdf/asdf.sh"
. "$HOME/.asdf/completions/asdf.bash"
Restart your shell, then install the Node plugin:
asdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs.git
# Install a Node version
asdf install nodejs latest
asdf install nodejs 22.14.0
# Set the global default
asdf global nodejs 22.14.0
# Set a project-local version (writes .tool-versions)
asdf local nodejs 20.18.0
# List installed
asdf list nodejs
Output (asdf list nodejs):
20.18.0
*22.14.0
The .tool-versions file is multi-runtime:
nodejs 22.14.0
python 3.12.7
ruby 3.3.5
terraform 1.9.5
mise — modern asdf-compatible manager
mise (formerly rtx) is a Rust-based, asdf-compatible runtime manager. It reads the same .tool-versions files but has a much faster startup, more features (env management, tasks), and a richer plugin ecosystem. Pick mise over asdf for new projects.
# macOS — Homebrew
brew install mise
# Linux / WSL — curl installer
curl https://mise.run | sh
# Windows — winget
winget install jdx.mise
Activate in your shell (one-time):
# bash / zsh — add to rc file
eval "$(mise activate bash)" # or zsh
# fish
mise activate fish | source
Use it identically to asdf:
mise use --global node@22
mise use node@20.18.0 # project-local (writes .mise.toml or .tool-versions)
mise install # install everything in .mise.toml
mise list # show installed versions
mise current # show what's active in the current dir
Output (mise current):
node@22.14.0
python@3.12.7
mise's project config:
# .mise.toml
[tools]
node = "22"
python = "3.12"
[env]
NODE_ENV = "development"
DATABASE_URL = "postgres://localhost/dev"
[tasks.dev]
run = "npm run dev"
mise run dev
Output: (none — exits 0 on success)
Manual install (no version manager)
Sometimes you want a single Node version, system-installed, with no manager overhead. The official downloads are the simplest path — pre-compiled binaries straight from nodejs.org.
# Linux x64 — download + extract to /usr/local
VER=v22.14.0
curl -fsSL "https://nodejs.org/dist/$VER/node-$VER-linux-x64.tar.xz" \
| sudo tar -xJ -C /usr/local --strip-components=1
Output: (none — exits 0 on success)
which node
node --version
Output:
/usr/local/bin/node
v22.14.0
# Windows — MSI installer
winget install OpenJS.NodeJS.LTS
# Or download the .msi from https://nodejs.org and double-click
Output: (none — exits 0 on success)
# macOS — pkg installer (preferred for "system" install)
curl -O "https://nodejs.org/dist/v22.14.0/node-v22.14.0.pkg"
sudo installer -pkg node-v22.14.0.pkg -target /
Output: (none — exits 0 on success)
Linux distribution packages
Distro packages (apt, dnf, pacman) are convenient but ship older Node versions than nodejs.org. Use them only when you must — for example on a server where you can't run network installers.
Debian / Ubuntu
# Default repos (often old — e.g. Ubuntu 24.04 ships Node 18)
sudo apt-get update
sudo apt-get install -y nodejs npm
# NodeSource (current LTS)
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt-get install -y nodejs
Output:
Setting up nodejs (22.14.0-1nodesource1) ...
Fedora / RHEL / Rocky
# Default dnf module (older Node)
sudo dnf module install nodejs:22/common
# NodeSource (current LTS)
curl -fsSL https://rpm.nodesource.com/setup_22.x | sudo bash -
sudo dnf install -y nodejs
Output: (none — exits 0 on success)
Arch / Manjaro
sudo pacman -S nodejs npm
Output: (none — exits 0 on success)
Alpine (for Docker base images)
apk add --no-cache nodejs npm
Output: (none — exits 0 on success)
Path layout reference
Where Node lives after each install method — useful when diagnosing PATH conflicts or "wrong version" issues.
| Install method | Node binary | npm prefix | Global modules |
|---|---|---|---|
| Official .pkg (macOS) | /usr/local/bin/node | /usr/local | /usr/local/lib/node_modules/ |
| Homebrew (macOS Apple Silicon) | /opt/homebrew/bin/node | /opt/homebrew | /opt/homebrew/lib/node_modules/ |
| Homebrew (macOS Intel) | /usr/local/bin/node | /usr/local | /usr/local/lib/node_modules/ |
| winget (Windows) | C:\Program Files\nodejs\node.exe | C:\Program Files\nodejs | %APPDATA%\npm\node_modules\ |
| apt / dnf (Linux) | /usr/bin/node | /usr | /usr/lib/node_modules/ |
| nvm | ~/.nvm/versions/node/v<X>/bin/node | ~/.nvm/versions/node/v<X> | ~/.nvm/versions/node/v<X>/lib/node_modules/ |
| fnm | ~/.local/share/fnm/node-versions/v<X>/installation/bin/node | (same prefix) | (same prefix)/lib/node_modules/ |
| Volta | ~/.volta/tools/image/node/<X>/bin/node | (managed by Volta) | (managed by Volta) |
| asdf / mise | ~/.asdf/installs/nodejs/<X>/bin/node | (same prefix) | (same prefix)/lib/node_modules/ |
Inspect what's currently on PATH:
which -a node
Output:
/home/alice/.nvm/versions/node/v22.14.0/bin/node
/usr/bin/node
The first entry wins. If the wrong version runs, it's a PATH ordering bug.
Switching package manager via Corepack
Corepack ships with Node 16.9+. It activates the package manager declared in your package.json packageManager field, so every contributor uses the exact same version.
# One-time enable (idempotent)
corepack enable
# Prepare a specific version
corepack prepare pnpm@9.12.1 --activate
corepack prepare yarn@4.5.1 --activate
corepack prepare npm@10.9.2 --activate
Output: (none — exits 0 on success)
{
"packageManager": "pnpm@9.12.1"
}
Now pnpm install in this repo always uses pnpm 9.12.1 — even if the global pnpm is something else.
pnpm --version
Output:
9.12.1
See pnpm, yarn, and npm for the per-tool deep-dives.
Installing Bun (alternative runtime)
Bun is a Node-compatible JavaScript runtime, package manager, bundler, and test runner — all in one Zig-written binary. Useful as a faster Node replacement for new projects. Coexists with Node; pick per-project via shebang or per-command.
# Linux / macOS / WSL — official installer
curl -fsSL https://bun.sh/install | bash
# macOS — Homebrew
brew install oven-sh/bun/bun
# Windows — PowerShell
powershell -c "irm bun.sh/install.ps1 | iex"
# Verify
bun --version
Output:
1.2.14
See bun for the full deep-dive.
Installing Deno (secure runtime)
Deno is a secure-by-default JavaScript and TypeScript runtime by the original creator of Node. Sandboxes scripts behind explicit permission flags. Useful for one-off scripts and security-sensitive workloads.
# Linux / macOS / WSL
curl -fsSL https://deno.land/install.sh | sh
# macOS — Homebrew
brew install deno
# Windows — PowerShell
powershell -c "irm https://deno.land/install.ps1 | iex"
# Cargo (build from source)
cargo install deno --locked
# Verify
deno --version
Output:
deno 2.1.4 (stable, release, x86_64-unknown-linux-gnu)
v8 13.0.245.12
typescript 5.6.2
See deno for the full deep-dive.
Choosing between Node, Bun, and Deno
A quick decision table for picking a runtime on a new project.
| Project shape | Best pick | Why |
|---|---|---|
| Production server, big ecosystem | Node 22 LTS | Most stable; best library support |
| Greenfield service, max speed | Bun | Faster install + runtime; Node-compat |
One-off script with fetch() | Deno | Zero setup; permission-sandboxed |
| TypeScript-heavy library | Deno (publish to JSR) | Native TS; no build step |
| AWS Lambda / Vercel / Cloudflare | Node 22 or Workerd | Best platform support |
| Electron app | Node | Required by Electron |
| Internal tooling, modern team | Bun | Speed > compat trade-off acceptable |
See bun and deno for the per-runtime details.
Verify the full toolchain
After installing, sanity-check every tool you'll use.
node --version
npm --version
npx --version
corepack --version
Output:
v22.14.0
10.9.2
10.9.2
0.31.0
Confirm node_modules/.bin resolution works:
mkdir /tmp/probe && cd /tmp/probe
npm init -y >/dev/null
npm install --save-dev typescript
npx tsc --version
Output:
Version 5.5.4
Uninstalling Node
When switching from a system Node to a version-managed setup, remove the old install first. Mixing the two leads to "wrong version" bugs that are hard to diagnose.
Windows
winget uninstall OpenJS.NodeJS.LTS
# Then delete C:\Users\<You>\AppData\Roaming\npm (global modules)
Output: (none — exits 0 on success)
macOS — Homebrew
brew uninstall node
brew uninstall --ignore-dependencies node
rm -rf /usr/local/lib/node_modules /usr/local/include/node
Output: (none — exits 0 on success)
macOS — official .pkg
sudo rm /usr/local/bin/node /usr/local/bin/npm /usr/local/bin/npx
sudo rm -rf /usr/local/lib/node_modules /usr/local/include/node
sudo rm /etc/paths.d/40-node
Output: (none — exits 0 on success)
Linux — apt
sudo apt-get remove --purge nodejs npm
sudo apt-get autoremove
sudo rm -rf /usr/lib/node_modules
Output: (none — exits 0 on success)
Common pitfalls
- EACCES on
npm install -g— you're using a system Node and don't have write access to/usr/lib/node_modules. Switch to nvm/fnm/Volta or setprefix=~/.npm-globalin~/.npmrcand add~/.npm-global/binto PATH. Neversudo npm install -g. nvm: command not foundafter install — the install script edits your shell rc file, but the current shell hasn't sourced it yet. Restart the terminal orsource ~/.bashrc.- Wrong Node version in
cron/systemd— these don't read your shell rc. Use a full path:/home/alice/.nvm/versions/node/v22.14.0/bin/node. .nvmrcignored — nvm only switches on demand (nvm use). Addcdhooks to your shell rc to switch automatically, or use fnm/Volta which auto-switch.- Multiple Node installs fighting on PATH —
which -a nodeshows them all. Remove duplicates from~/.bashrc,~/.profile,/etc/paths.d/, and shell completion scripts. - WSL Node slower than Windows Node — running
nodein/mnt/c/...is slow because of the 9P filesystem. Keep your project under the Linux home (/home/...) when working in WSL. corepack enablefailing on shared CI — Corepack writes shims to the Node bin directory. On hosted CI with read-only Node, install pnpm/yarn explicitly:npm install -g pnpm@9.- Stale
nvminstall — oldnvmversions don't know about newer LTS lines. Update withnvm install-latest-npmand refresh nvm itself by re-running the install script. - Volta and nvm both installed — Volta's shim and nvm's PATH manipulation collide. Pick one; uninstall the other.
fnm: command not foundin new shell — you added the eval to~/.bashrc, but your terminal opens login shells, which read~/.profile. Addfnm env --use-on-cdto both orsource ~/.bashrcfrom~/.profile.
Real-world recipes
Per-project Node version with auto-switch
The recommended setup: a .nvmrc (or .node-version) at the repo root + a shell hook that switches when you cd in. Below uses fnm's built-in --use-on-cd.
# Repo root
echo "22.14.0" > .nvmrc
git add .nvmrc
Output: (none — exits 0 on success)
# Shell rc (one-time)
eval "$(fnm env --use-on-cd)"
cd ~/projects/my-app # fnm auto-switches to 22.14.0
node --version
Output:
Using Node v22.14.0
v22.14.0
CI matrix across Node LTS versions
Test against every supported LTS in parallel. Pin minor versions to keep the matrix stable.
# .github/workflows/test.yml
jobs:
test:
strategy:
matrix:
node: [20.18, 22.14, 24.0]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: npm
- run: npm ci
- run: npm test
node --version
Output:
v22.14.0
Dockerfile with pinned Node + pnpm
Multi-stage build: deps in a cached layer, app in a thin runtime image.
# syntax=docker/dockerfile:1
FROM node:22.14-alpine AS base
RUN corepack enable && corepack prepare pnpm@9.12.1 --activate
WORKDIR /app
FROM base AS deps
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile --prod
FROM base AS runner
COPY --from=deps /app/node_modules ./node_modules
COPY . .
CMD ["node", "dist/server.js"]
docker build -t my-app .
docker run --rm my-app
Output:
Server listening on http://0.0.0.0:3000
Switch between Node 22 and Bun on the same project
Per-shebang selection — Bun for hot-path scripts, Node for the canonical runtime.
# bun-only entry (note the shebang)
#!/usr/bin/env bun
console.log(Bun.version);
chmod +x ./script.ts
./script.ts
node script.ts # uses Node (errors on Bun-only APIs)
bun script.ts # uses Bun
Output:
1.2.14
See bun for what's safe to call.
Bootstrap a clean Mac dev machine
A condensed checklist a teammate can run after a fresh macOS install.
# Xcode CLT (compilers, git)
xcode-select --install
# Homebrew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Node via fnm + Corepack-managed pnpm
brew install fnm
echo 'eval "$(fnm env --use-on-cd)"' >> ~/.zshrc
exec zsh
fnm install --lts
corepack enable
corepack prepare pnpm@latest --activate
# Optional: Bun and Deno
brew install oven-sh/bun/bun deno
node --version && pnpm --version && bun --version && deno --version | head -1
Output:
v22.14.0
9.12.1
1.2.14
deno 2.1.4 (stable, release, aarch64-apple-darwin)
Pin everything for a team
engines + packageManager + .nvmrc covers every developer regardless of which version manager they use.
{
"engines": {
"node": ">=22.14 <23",
"pnpm": ">=9.12"
},
"packageManager": "pnpm@9.12.1"
}
22.14.0
# After cloning, contributors run:
fnm use # or nvm use, or asdf install, or mise install
corepack enable
pnpm install
Output: (none — exits 0 on success)
Roll back a Node upgrade
A new Node release broke your project. Switch back without reinstalling everything.
fnm install 22.10.0
fnm use 22.10.0
npm ci
Output:
Installed Node v22.10.0
Using Node v22.10.0
added 247 packages in 4s
Pin in .nvmrc so the rollback is shared with the team:
echo "22.10.0" > .nvmrc
git add .nvmrc && git commit -m "pin: roll node back to 22.10.0"
Output: (none — exits 0 on success)