TechEarl

How to Install Node.js on Linux, macOS, and Windows

How to install Node.js the right way on Linux, macOS, and Windows. Covers nvm, fnm, Volta, the official nodejs.org installer, apt/dnf via NodeSource, Homebrew, winget, Chocolatey, Docker, and how to verify the install.

Ishan Karunaratne⏱️ 13 min readUpdated
Share thisCopied
Installing Node.js on Linux, macOS, and Windows using a version manager and the official installer, with node --version verifying a fresh install

Installing Node.js is genuinely simple, but the best way to install it depends on the machine: on a development laptop you almost always want a version manager (so you can hold several Node majors side by side and never need sudo for a global package), whereas on a single-purpose server or a locked-down work laptop the official installer or your OS package manager is the pragmatic choice. I have set Node up on my own machines, on production boxes, and inside CI more times than I can count, and the one mistake that bites people is reaching for apt install nodejs on day one and ending up stuck on a version that is a year behind. Below is how to install Node.js on each OS the way I actually do it, with the verification step and the most common first-install snags.

Current Node.js
26.2.0

Latest release with newest features. Best for experimentation.

Latest LTS
24.16.0

Long-Term Support — the version to use in production.

How do I install Node.js?

The fastest reliable way to install Node.js is with a version manager. On Linux or macOS, install fnm (curl -fsSL https://fnm.vercel.app/install | bash), then fnm install --lts and fnm use lts-latest. On Windows, winget install Schniz.fnm then the same fnm install --lts. If you do not want a version manager, download the LTS installer from nodejs.org/en/download and run it (Windows .msi, macOS .pkg, Linux tarball). On Ubuntu/Debian use NodeSource (curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash - && sudo apt-get install -y nodejs) rather than the stale distro nodejs package. On macOS Homebrew, brew install node. After any method, verify with node --version and npm --version in a fresh terminal. Choose LTS, not Current, unless you have a specific reason to be on the bleeding edge.

Jump to:

LTS vs Current: which one to install

Node.js ships two release lines at once, and picking the wrong one is the most common first-install misstep:

LineVersionsReleasedSupportedInstall this if
LTSEven majors (20, 22, 24)April30 monthsYou are building anything real: apps, libraries, production services
CurrentOdd majors (21, 23, 25)October6 monthsYou want to test new V8/language features before they reach LTS

Pick LTS. Almost every npm library declares engines.node ranges that target LTS, and the Active LTS line is what gets the longest support window. Current is fine to kick the tyres on, but it goes end-of-life in six months and you will be reinstalling. The live card above pulls the exact current LTS and Current numbers from the Node.js release feed.

Which installation method should I use?

MethodPlatformsMultiple versions?Needs sudo/admin?Best for
fnmLinux, macOS, WindowsYesNoModern default; fast, cross-platform
nvmLinux, macOSYesNoThe most-documented option (Bash script)
VoltaLinux, macOS, WindowsYes (repo-pinned)NoTeams pinning Node per project via package.json
nodejs.org installerLinux, macOS, WindowsNoYesLocked-down or single-purpose machines
NodeSource (apt/dnf)LinuxNoYesServers, Docker, current versions from a repo
HomebrewmacOS, LinuxLimitedNoMac devs already living in brew
winget / ChocolateyWindowsNoYesWindows machines, scripted provisioning
DockerAnywherePer imageNoContainers; "install" means the FROM line

If you only take one recommendation: use fnm on a personal machine, Volta on a team repo, and the official installer or NodeSource where you cannot install a version manager.

Install on Linux (Ubuntu, Debian, Fedora, RHEL)

Do not use the distro's own nodejs package as your first move. Ubuntu's apt repo, for example, often ships a Node major that is well behind the current LTS, and you cannot easily switch off it later. Use NodeSource, which mirrors the official release line.

Ubuntu / Debian (apt):

bash
# Latest LTS
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt-get install -y nodejs

# Or a specific major (e.g. Node 22)
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt-get install -y nodejs

Fedora / RHEL / Rocky / Alma (dnf):

bash
curl -fsSL https://rpm.nodesource.com/setup_lts.x | sudo bash -
sudo dnf install -y nodejs

NodeSource installs npm alongside Node, so you do not need a separate npm package (and on Debian/Ubuntu installing the distro npm separately can actually conflict). Verify with node --version && npm --version.

If you would rather not pipe a script into bash as root (a fair instinct on a shared server), install fnm under your own user instead, see the version-manager section below. It needs no root and gives you version switching for free.

Install on macOS

Two good options on macOS: Homebrew, or a version manager.

Homebrew (simplest if you already use brew):

bash
brew update
brew install node          # latest stable
brew install node@22       # a specific major
node --version

Homebrew's node formula tracks the latest stable, which can be a Current (odd) major rather than LTS. If you need to stay on LTS, install the pinned formula (node@22) and brew link it.

fnm via Homebrew (my preference, gives you version switching):

bash
brew install fnm
echo 'eval "$(fnm env --use-on-cd)"' >> ~/.zshrc
source ~/.zshrc
fnm install --lts
fnm use lts-latest

The macOS .pkg from nodejs.org is also a perfectly good choice and is a Universal binary, running natively on both Apple Silicon and Intel. See the official-installer section.

Install on Windows

Windows has more options than the other platforms because the original Unix nvm does not run there. Pick one and stick to it; mixing two Node installers on Windows is the fastest route to "which node.exe is on PATH" confusion.

ToolInstall commandVersion switching?
fnmwinget install Schniz.fnmYes (best modern choice)
wingetwinget install OpenJS.NodeJS.LTSNo
Chocolateychoco install nodejs-ltsNo
nvm-windowsfrom coreybutler/nvm-windowsYes
Voltawinget install Volta.VoltaYes (repo-pinned)
Direct .msifrom nodejs.org/en/downloadNo

For a development machine, install fnm and enable auto-switching in PowerShell by adding this to your $PROFILE:

powershell
winget install Schniz.fnm
fnm env --use-on-cd | Out-String | Invoke-Expression
fnm install --lts
fnm use lts-latest

For a build server or a quick one-off, winget install OpenJS.NodeJS.LTS is the least-friction path. The .msi installer also offers a "Tools for Native Modules" checkbox that installs the Python and Visual Studio C++ workloads needed to compile native addons (bcrypt, sharp, and friends); tick it if your projects use any.

A version manager installs Node under your home directory, lets you hold several majors side by side, switches between them per project, and means you never need sudo for a global npm package. This is how I set up every dev machine.

fnm (Fast Node Manager, a Rust rewrite of nvm; cross-platform and fast):

bash
# Linux / macOS
curl -fsSL https://fnm.vercel.app/install | bash

# add the shell hook (zsh shown; use ~/.bashrc for bash)
echo 'eval "$(fnm env --use-on-cd)"' >> ~/.zshrc
source ~/.zshrc

fnm install --lts          # install latest LTS
fnm use lts-latest         # activate it
fnm default lts-latest     # default for new shells

nvm (the original; Bash script, Linux and macOS only):

bash
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.4/install.sh | bash
source ~/.bashrc           # or ~/.zshrc on macOS

nvm install --lts          # install latest LTS
nvm use --lts              # activate for this shell
nvm alias default --lts    # default for new shells

Volta (transparent, repo-pinned versions, great for teams):

bash
# Linux / macOS
curl https://get.volta.sh | bash

volta install node@lts     # globally available
# inside a project:
volta pin node@22          # writes a "volta" key into package.json

With any of these, pinning a version per project (.nvmrc, .node-version, or the volta key in package.json) keeps every developer and CI on the same major. For a head-to-head on which to choose, see nvm vs fnm vs Volta.

Install via the official nodejs.org installer

For machines where a version manager is not an option (corporate-managed laptops, kiosks, single-purpose servers), the official installer is the right answer:

  1. Go to nodejs.org/en/download
  2. Choose LTS unless you specifically need Current
  3. Pick your OS and architecture (Windows .msi, macOS .pkg Universal, Linux tar.xz)
  4. Run it; it installs Node and the bundled npm, and adds them to PATH
  5. Open a new terminal (the old one has a stale PATH) and verify
bash
node --version
npm --version

This method does not give you side-by-side versions: to switch majors later you would reinstall, or install a version manager and let it take over. The macOS .pkg is a Universal binary (native on both Apple Silicon and Intel); the Windows .msi includes the optional native-module build-tools step.

Install in Docker

Inside a container you do not install Node at all, you base the image on an official node: image and pin the version in the FROM line:

dockerfile
# Pin to an LTS major. Recommended for production.
FROM node:22-alpine

# Pin to an exact patch for fully reproducible builds.
FROM node:22.11.0-alpine

# Tracks whatever the current LTS is (convenient, less reproducible).
FROM node:lts-alpine

Use -alpine for the smallest image (musl libc, around 50 MB) or -slim (Debian, glibc) when a dependency needs glibc compatibility. Always pin to a major or major.minor in production: never node:latest, which follows Current and will flip majors on you every October. For the full multi-stage pattern, see How to Dockerize a Node.js App.

Verify the install

Whatever method you used, confirm it landed:

bash
node --version     # e.g. v22.11.0
npm --version      # e.g. 10.9.0
which node         # where node is installed (on Windows: where node)

A successful install prints a Node version on the first line and an npm version on the second. which node (or where node) tells you which install is on PATH, which is the diagnostic that resolves nearly every "but I installed it" puzzle: a version manager will print a path under ~/.fnm, ~/.nvm, or ~/.volta; the official installer prints /usr/local/bin/node (macOS/Linux) or a Program Files path (Windows). For a deeper look at reading and comparing versions, see how to check your Node version. When the time comes to move up a major, updating Node.js walks through every upgrade path and the native-module rebuild step.

A quick smoke test that Node actually runs:

bash
node -e "console.log('node ' + process.version + ' works')"
# node v22.11.0 works

Troubleshooting a fresh install

node: command not found right after installing. The shell has a stale PATH. Open a brand-new terminal. If it persists with a version manager, you missed the shell-init line: fnm needs eval "$(fnm env --use-on-cd)" in ~/.zshrc or ~/.bashrc; nvm needs its sourcing snippet (the installer appends it, but only to one rc file).

A very old version came in via apt. You installed the distro nodejs package instead of NodeSource. Remove it (sudo apt-get remove -y nodejs) and reinstall through NodeSource as shown above, or switch to fnm under your user.

EACCES: permission denied installing a global npm package. Your Node lives in a root-owned directory (typical with a system or Homebrew install) and global npm wants to write there. The clean fix is to use a version manager so npm globals live under your home directory; the quick fix is npm config set prefix ~/.npm-global and add ~/.npm-global/bin to PATH. Do not sudo npm install -g, that compounds the problem.

Homebrew installed a Current (odd) major when you wanted LTS. brew install node tracks latest stable. Install the pinned LTS formula instead: brew install node@22 && brew link --overwrite node@22.

node-gyp errors when a dependency compiles. Native build tools are missing. Linux: sudo apt-get install -y build-essential python3. macOS: xcode-select --install. Windows: re-run the .msi and tick the native-modules option.

Two managers fighting. If node --version and fnm current (or nvm current) disagree, you have more than one Node manager installed. Pick one, uninstall the others, and reload the shell.

FAQ

See also

  • How to update Node.js: once Node is installed, this covers every upgrade path (nvm, fnm, Volta, the installer, Docker, CI) and the native-module rebuild people forget.
  • nvm vs fnm vs Volta: the head-to-head if you are choosing a version manager and want speed, auto-switching, and pinning compared.
  • Checking your Node version: how to read node --version, compare it to a project's requirements, and tell which install is active.
  • How to Dockerize a Node.js App: the multi-stage Dockerfile behind the FROM node: line in the Docker section above.

Sources

Authoritative references this article was fact-checked against.

TagsNode.jsJavaScriptnvmfnmVoltaInstallationCLIDockerDevOps

Found this useful? Pass it on.

Copied

Ishan Karunaratne

Tech Architect · Software Engineer · AI/DevOps

Tech architect and software engineer with 20+ years building software, Linux systems, and DevOps infrastructure, and lately working AI into the stack. Currently Chief Technology Officer at a healthcare tech startup, which is where most of these field notes come from.

Keep reading

Related posts

How to Uninstall Node.js (Every Install Method)

How to uninstall Node.js cleanly on Linux, macOS, and Windows: version managers (nvm, fnm, Volta, n), the official installer, apt/dnf/brew/winget/choco, Docker, plus the leftover npm/cache/PATH files everyone forgets to delete.

How to Disable Root Login on Linux

Disable direct root login over SSH and on the console, lock the root password, and move everyone to a normal account plus sudo, without locking yourself out.