How to Install Node.js and npm: nvm, fnm, and Fix Permission Errors
Use an LTS Node version, switch runtimes with nvm or fnm, and stop EACCES errors when installing global packages.
Languages & runtimes Beginner 6 min read
·
Node ships with npm so one install gives you a runtime and a package installer. You use nvm (or similar) because projects often require different Node major versions; switching globally avoids “works on my machine” CI failures. You run node -v and npm -v after switching to prove the shell picked up the intended binaries.
This guide pairs well with REST APIs and curl when you build HTTP services.
Install nvm (macOS / Linux)
Why nvm instead of only system packages: Distro Node versions lag upstream; nvm installs tarballs per user without sudo.
Follow the install script at the nvm repository (it edits your shell rc to load nvm), then open a new terminal and run:
nvm install --lts
nvm use --lts
node -v
npm -v
Check: Versions print without “command not found”. which node should point under your home directory (nvm’s shims), not only /usr/bin.
Windows: nvm-windows
Why a separate project: Unix nvm relies on shell functions; Windows needs a native version manager or the official MSI from nodejs.org.
Use nvm-windows for per-project Node versions, or install the official MSI from nodejs.org if you only need one global version.
fnm as a fast alternative
Why some teams prefer fnm: It is a single binary, very fast for switching versions in CI and locally—same mental model as nvm.
fnm: fnm install --lts and fnm use.
Global installs and EACCES
Why sudo npm install -g is a bad habit: It mixes root-owned files into prefixes your user cannot update later, breaking upgrades. nvm keeps npm’s global prefix inside your home; project-local npm install avoids globals entirely for tools like linters.
Avoid sudo npm install -g. Instead use a Node version manager or configure npm’s global directory to a user-owned path. Project-local installs (npm install --save-dev) avoid most permission issues.
package.json and lockfiles
Why commit the lockfile: package.json allows ranges; the lockfile pins the exact graph npm resolved—without it, CI might fetch newer transitive deps and break builds.
Commit package.json and your lockfile (package-lock.json for npm, yarn.lock, or pnpm-lock.yaml) so CI and teammates get identical dependency trees.
Related guides
Store API keys in env vars: Environment variables and secrets.
Frequently asked questions
npm, npx, yarn, pnpm—which should I use?
npm ships with Node. npx runs package binaries without global install. Yarn and pnpm are popular alternatives with different disk and install strategies; pick one per repo and stay consistent.
What is the difference between dependencies and devDependencies?
Runtime packages belong in dependencies; build/test tools in devDependencies. Production installs can omit devDependencies with npm ci --omit=dev.
How do I update Node?
With nvm: nvm install lts then nvm alias default node. Always retest projects after a major version bump.
Corepack?
Node can manage Yarn/pnpm versions via Corepack; enable if your team standardizes on it.