What the end of life of Node.js 20 actually changes
Node.js follows a public cycle: an even-numbered line enters Active LTS in October, moves to Maintenance LTS a year later, then stops. Node.js 20 entered Maintenance LTS on October 22, 2024 and reached its end of life on April 30, 2026. Since then, a runtime flaw is no longer fixed upstream: not in the V8 engine, not in the bundled TLS library, not in the HTTP parser. The code still runs, but every published vulnerability stays open there.
What a frozen runtime costs
- Unpatched flaws — vulnerabilities published after April 30, 2026 will receive no upstream fix; only a version upgrade closes them.
- Packages dropping off — maintainers raise the
enginesfield of theirpackage.jsontowards the lines still supported; a fresh install eventually fails, or pins you to old versions. - Frozen base images — a
node:20image gets no more fixes, neither for the runtime nor for the system packages beneath it; your vulnerability scan flags it on every build. - Audits that stall — an end-of-life runtime is an unaccepted risk in security reviews and vendor questionnaires.
- Emergency migration — the wider the gap, the more the switch costs; carrying it out today is a planned project, being forced into it after an incident is not.
Find out what your servers actually run
node -v in a shell tells only part of the story: the binary in your session is not necessarily the one serving production. A process manager keeps the version it started with, nvm installs several per user, a Dockerfile freezes its own in a FROM node:20. Inventory every source: pm2 jsonlist for live processes, a grep -r 'FROM node' across your Dockerfiles, the engines field of every package.json, and the version pinned in CI or in your managed runtimes.
Migrate without breaking production
Take inventory before touching anything else
List, application by application: the version actually running, the process manager, the base image, and the native dependencies — node-gyp, sharp, better-sqlite3. They set the schedule, not your code: a module compiled with no prebuilt binary for the new line blocks the whole switch.
Pick the target version
Node.js 24 has been in Active LTS since October 28, 2025 and its end of life is set for April 30, 2028: that is the default target. Node.js 22, in Maintenance LTS since October 21, 2025, is supported until April 30, 2027 — an acceptable stopover if a native dependency is not ready yet, never a destination. Node.js 26 only enters LTS on October 28, 2026: not in production today.
Replay the test suite on the target version
Add the target version to your CI matrix before removing the old one: both must go green at the same time. Reinstall from cold (rm -rf node_modules then npm ci) to force native modules to recompile: that is where breakage shows up, rarely in unit tests. Read the deprecation warnings — they are the errors of the next line.
Switch one service at a time
nvm install 24 installs the new line alongside the old one without removing it. Then restart the process manager daemon (pm2 update) and recreate the service (pm2 delete then pm2 start): a plain pm2 restart keeps the recorded interpreter. Regenerate the startup script (pm2 startup, pm2 save), then check with pm2 jsonlist that the reported version changed.
Monitor, then clean up
Keep error logs and the memory curve in view for 48 hours: a change of V8 line shifts the garbage collector profile more often than it breaks an API. Once the switch is confirmed, uninstall Node.js 20 so that no deployment brings it back by accident. If you would rather start from a clean server than migrate in place, our guide deployer-nodejs-vps covers the full chain.
Pin the version, do not just migrate it
A runtime upgrade is the right moment to tighten what surrounds it. Pin the target version in an .nvmrc versioned with the code and align the engines field of your package.json: the server, the CI, and developer machines stop drifting apart in silence — that drift is what lets an end-of-life runtime survive its own migration. Run the application under a dedicated user, with no write access to its code directory.
Make end of life an appointment, not a surprise
The Node.js schedule is public: an even-numbered line becomes LTS in October, then stops thirty months later, always on April 30. Node.js 22 stops on April 30, 2027, Node.js 24 on April 30, 2028: both deadlines can go into your operations calendar today. Recurring and budgeted, a version upgrade takes a few days a year; handled as an emergency, it costs far more.