Installing & Version Management
Consistent Node versions across laptops, CI, and production prevent "works on my machine" failures - pin Active LTS and enforce it at install time.
Recipe
Quick-reference recipe card - copy-paste ready.
// package.json
{
"engines": {
"node": ">=24.18.0 <25",
"npm": ">=10.0.0"
},
"engineStrict": true
}# .nvmrc
24.18.0When to reach for this:
- Onboarding a new developer to an existing Node service
- Upgrading from Node 22 Maintenance LTS to Node 24 Active LTS
- CI pipelines that silently pick up the wrong runtime
- Monorepos where multiple services share one Node version policy
Working Example
# Install fnm (macOS/Linux) - fast, cross-shell version manager
curl -fsSL https://fnm.vercel.app/install | bash
fnm install 24.18.0
fnm use 24.18.0
node --version # v24.18.0// package.json excerpt
{
"name": "billing-api",
"type": "module",
"engines": { "node": ">=24.18.0 <25" },
"scripts": {
"preinstall": "node scripts/check-node-version.mjs"
}
}// scripts/check-node-version.mjs
const required = 24;
const major = Number(process.versions.node.split('.')[0]);
if (major !== required) {
console.error(`Node ${required}.x required, got ${process.version}`);
process.exit(1);
}What this demonstrates:
.nvmrc/.node-versionfiles integrate with fnm, nvm, and asdfenginesdocuments the supported range for humans and tooling- A
preinstallscript fails fast beforenpm installdownloads packages - Pinning
<25excludes Current (odd-numbered) releases unsuitable for production
Deep Dive
How It Works
- Version managers install multiple Node binaries side by side and swap the active one via shell hooks.
enginesis advisory unlessengine-strict=truein.npmrcor you add enforcement scripts.- Docker/Kubernetes should use official
node:24.18.0-bookworm-slimimages - notnode:latest. - Volta pins per-project versions in
package.jsonvoltafield and auto-switches oncd.
Version Manager Comparison
| Tool | Speed | Pinning model | Best for |
|---|---|---|---|
| fnm | Fast (Rust) | .nvmrc, shell hook | Daily dev on macOS/Linux |
| nvm | Moderate | .nvmrc, widely known | Legacy docs and tutorials |
| Volta | Fast | package.json volta block | Teams wanting zero manual use |
| asdf | Plugin-based | .tool-versions | Polyglot repos (Node + Ruby + Go) |
TypeScript Notes
// Read the runtime version at startup and expose it in /health
export function runtimeInfo(): { node: string; platform: string } {
return {
node: process.version,
platform: `${process.platform}-${process.arch}`,
};
}Gotchas
- Using
node:latestin Docker - every build may get a different major version. Fix: pinnode:24.18.0-bookworm-slimwith a digest. - Ignoring Maintenance LTS end dates - Node 22 enters end-of-life in 2027; plan upgrades before security patches stop. Fix: track the Node release schedule.
engineswithout enforcement - npm warns but installs anyway by default. Fix:engine-strict=truein.npmrcor CI gate.- Global npm packages tied to one Node version - upgrading Node breaks global CLIs. Fix: use
npx,pnpm dlx, or project-local devDependencies. - Mismatched npm and Node - Node 24 ships npm 10; older npm with newer Node causes subtle lockfile differences. Fix: use the bundled npm or pin via Corepack.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Volta | Automatic per-project switching | You need Windows support with minimal setup friction |
| asdf | One tool for many languages | Node-only teams wanting the simplest path |
| Container-only (no local manager) | Devcontainers / Docker-only workflows | Fast local iteration without container overhead |
System package manager (apt, brew) | Quick one-off installs | Production parity or multi-version switching |
FAQs
Which Node version should production run?
Node.js 24.18.0 (Active LTS) for new deployments. Node 22.23.1 (Maintenance LTS) is acceptable during migration but plan to upgrade before maintenance ends.
What is the difference between fnm and nvm?
Both swap Node versions via shell hooks. fnm is faster (written in Rust) and supports the same .nvmrc file format.
Should I commit .nvmrc?
Yes. Commit .nvmrc or .node-version so every developer and CI job resolves the same version.
How does Volta pin versions?
Add a volta section to package.json:
"volta": { "node": "24.18.0", "npm": "10.9.0" }Volta switches automatically when you enter the project directory.
What does engineStrict do?
When engine-strict=true in .npmrc, npm refuses to install if the running Node/npm does not satisfy engines.
Can I use Corepack for pnpm or Yarn?
Yes. corepack enable activates the package manager version declared in packageManager field:
"packageManager": "pnpm@9.15.0"How do I upgrade Node in CI?
Match CI to .nvmrc:
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'What about Windows developers?
fnm, nvm-windows, and Volta all support Windows. Document one canonical tool in the team README.
Should odd-numbered releases like Node 25 be used?
No for production. Odd releases are "Current" and short-lived. Use Active LTS (even numbers) for services.
How do I check what is installed globally?
npm list -g --depth=0Prefer project-local devDependencies over globals for reproducibility.
Does Alpine Linux need special Node images?
Alpine uses musl libc - some native addons need extra build steps. bookworm-slim (Debian) is safer for native modules.
How do I migrate from Node 20 to 24?
Upgrade dev machines and CI first, run the test suite, check deprecated API warnings with NODE_OPTIONS=--pending-deprecation, then roll production.
Related
- Node.js Release & LTS Policy - Current vs Active vs Maintenance
- Node.js Basics - first commands after install
- Running Scripts & Shebang - executable bins and shebang lines
- Reading the Official Docs & Diagnostics - when upgrades surprise you
Stack versions: This page was written for Node.js 24.18.0 (Active LTS), npm 10+, TypeScript 5.6+, Express 5, Fastify 5, and NestJS 11.