Upgrade to Node.js 20
Node.js 20 is the active LTS and the recommended target for anything still on Node 16 (EOL) or Node 18 (maintenance). The risk is concentrated in native-module rebuilds, OpenSSL 3 behavior, and removed deprecated APIs — not in rewriting application code.
Last verified · Updated May 22, 2026
Node 20 is the active LTS and a recommended upgrade for apps on Node 16 (EOL) or Node 18 (maintenance). Most breakage comes from native-module ABI rebuilds, OpenSSL 3 stricter defaults, and removed deprecated APIs — not from rewriting code. Budget half a day to three days depending on how many native dependencies you ship.
Who should upgrade
- Anyone still on Node 16 — it is end-of-life and receives no security patches.
- Teams on Node 18 wanting the stable node:test runner, a newer V8, and the longest remaining LTS window.
- Services that want built-in fetch and to drop a node-fetch/axios polyfill dependency.
Who should wait
- Apps blocked by a critical native dependency without a Node 20-compatible prebuilt binary.
- Builds pinned to an OpenSSL 1.1 behavior that breaks under OpenSSL 3's stricter defaults until the code is fixed.
What changed
- Stable built-in test runner via the node:test module — no external test framework required for basic suites.
- Global fetch, Request, Response, and Headers are available without a polyfill.
- Several previously --experimental flags graduated toward stable.
- Experimental Permission Model (--experimental-permission) to restrict file system, child process, and worker access.
- Newer V8 engine with updated language and performance features.
- Ships with an updated bundled npm and is built against OpenSSL 3.
⚠ Rebuild native modules before debugging runtime failures
A new Node major bumps the V8/N-API ABI, so any package with native bindings (binding.gyp, prebuilt .node files) must be reinstalled or rebuilt with
npm rebuild. Many 'mysterious' crashes right after an upgrade are stale native addons, not application bugs.
Recommended upgrade path
Fast path for a service moving onto the Node 20 LTS
# 1. Pin the new version locally
nvm install 20 && nvm use 20
node --version
# 2. Reinstall and rebuild native modules against the new ABI
rm -rf node_modules && npm ci
npm rebuild
# 3. Run the test suite on Node 20
npm testLTS support matrix
| Version | Status | LTS | Released | End of life |
|---|---|---|---|---|
| Node 16.x | End-of-life | Yes | 2021-04-20 | 2023-09-11 |
| Node 18.x | Maintenance | Yes | 2022-04-19 | 2025-04-30 |
| Node 20.x | Active LTS | Yes | 2023-04-18 | Active — target this |
AI-assisted migration workflow
Upgrade this project to Node.js 20 LTS. First inspect engines.node, .nvmrc, the CI node-version matrix, the Docker base image, and any native dependencies. Then bump the engine and pin .nvmrc, update CI and Docker, run `npm ci` followed by `npm rebuild`, and run the test suite. Triage failures: rebuild native modules first, then check for removed deprecated APIs and OpenSSL 3 fallout. Run install and tests after each step and report before continuing.Safety: Incremental edits only. Pause for review after the engine/CI changes and after the native rebuild. Do not silently upgrade unrelated dependencies.
PR review checklist
- engines.node, .nvmrc, the CI matrix, and the Docker base image all reference Node 20
- node_modules was reinstalled and native modules rebuilt against the new ABI
- No removed/deprecated Node APIs remain in changed code paths
- Any node-fetch/axios used only as a fetch polyfill was reviewed against built-in fetch
- No new Node deprecation warnings appear in the test output
Rollback strategy
- Keep the engine bump, CI/Docker changes, and native rebuild in separate commits.
- Revert .nvmrc and the Docker base image to the previous LTS and reinstall if a blocking native incompatibility appears.
- Hold the upgrade behind a release branch until the full test suite is green on Node 20 in CI.
Common errors
- 'Module did not self-register' or 'NODE_MODULE_VERSION' mismatch — rebuild native modules with npm rebuild.
- TLS/crypto errors after OpenSSL 3 — see the fix-node-dependency-errors workflow.
- 'engine "node" is incompatible' from a dependency — find a Node 20-compatible release or replacement.
Related migration paths
- Migrate Node.js CommonJS to ES Modules — Next step forward
- Node.js 16 to Node.js 20 Migration Guide — Upgrade path to same target
- Node.js 18 to Node.js 20 Migration Guide — Upgrade path to same target
- Fix Node.js Dependency Errors — Earlier step in the upgrade chain
Official sources
- Node.js releases — nodejs/node (reliability 92%)
- Node.js 20 is now available — nodejs.org (reliability 98%)
- Node.js ECMAScript Modules — nodejs.org (reliability 98%)
Copy-ready AI prompts
Structured prompts for an AI coding assistant. Inspect first, then execute incrementally, and keep a human in the review loop.
Repo inspection: Repo inspection prompt
You are helping with a Node.js migration: upgrade to Node.js 20 LTS.
Do not edit files yet. First inspect the repository and report:
1. The current Node version targets: the "engines.node" field in package.json, the .nvmrc / .node-version file, and the node-version used in CI (.github/workflows/*.yml) and any Dockerfile base image.
2. The module system: whether package.json sets "type": "module", and the mix of .js / .cjs / .mjs files.
3. Native modules (packages with binding.gyp or install scripts) that must be rebuilt against the new ABI.
4. Usage of deprecated or removed APIs surfaced by the new major's deprecations (e.g. legacy url.parse patterns, the old assert API, removed crypto/buffer constructors).
5. The install, build, and test commands and whether the lockfile is committed.
Return: a risk summary, the highest-risk files and dependencies, a suggested migration order, the commands to run before editing, and any questions that need human confirmation.Safety: Inspection only. The agent must not modify files in this step.
Works with Claude Code, Cursor, GitHub Copilot.
Migration execution: Migration execution prompt
Perform the migration (upgrade to Node.js 20 LTS) one concern at a time.
Work in this order and pause for review after each: (1) bump "engines.node" in package.json and pin the new version in .nvmrc, (2) update every CI matrix entry and Docker base image to the new Node version, (3) reinstall and rebuild native modules with `npm rebuild`, (4) run the test suite and triage runtime failures, (5) replace any deprecated/removed APIs surfaced during inspection.
After each step run the project's install and test commands and report results before continuing. Do not bundle unrelated refactors or upgrade dependencies that are not required by the Node bump.Safety: Apply changes incrementally and keep each step reviewable. Rebuild native modules before assuming a failure is a code problem.
Works with Claude Code, Cursor, GitHub Copilot.
Test plan
Commands
node --versionnpm cinpm rebuildnpm testnpm run build
Manual checks
- Native modules: confirm packages with native bindings load without ABI/version errors at runtime.
- Startup: boot the app on the new Node version and watch for new deprecation warnings.
- Globals: verify code relying on built-in fetch, the test runner, or other newly-stable APIs behaves the same locally and in CI.
Regression risks
- Native addons compiled against the old ABI failing to load until rebuilt.
- Deprecated APIs removed in the new major breaking code paths not covered by tests.
- Dependencies that declare an engines range excluding the new Node version.
Acceptance criteria
- Install, build, and the full test suite pass on the target Node version.
- CI, .nvmrc, and the Docker image all reference the same Node version.
- No new Node deprecation warnings appear in the test output.
Frequently asked questions
Is upgrading to Node 20 a hard upgrade?
For most services it is straightforward — the bulk of the work is updating the engine pin, CI matrix, and Docker image, then reinstalling and rebuilding native modules. Effort scales with how many native dependencies you ship and whether any code depends on OpenSSL 1.1 behavior or APIs removed in newer majors.
Do I have to remove node-fetch now that fetch is built in?
No. Built-in fetch is available globally on Node 18+ and stable on Node 20, but keeping a node-fetch/axios dependency still works. Removing the polyfill is an optional cleanup you can do incrementally once you have confirmed the built-in matches your usage.