Back to blog

// OSSeva Blog

Migration

Upgrade Node.js 18 or 20 to 22 or 24: Breaking Changes, Steps and Rollback

Matt Reynolds11 min read

The short answer

If you run Node.js 18 or 20, you are already past end of life. Node.js 18 stopped receiving fixes on 30 April 2025 and Node.js 20 on 30 April 2026, according to the Node.js release schedule. Move to Node.js 24 unless a dependency blocks it. Node.js 24 (Krypton) is an LTS line supported until 30 April 2028. Node.js 22 (Jod) is a valid stepping stone, but it is already in maintenance and reaches end of life on 30 April 2027, about seven months from now.

Most applications need three kinds of work: replacing a short list of removed or deprecated APIs (the Node.js project publishes codemods for most of them), rebuilding native add-ons, and checking TLS keys and certificates against the stricter OpenSSL defaults in Node.js 24. The rest of this guide covers each one.

Where each release line stands on 5 October 2026

LineStatusEnd of lifeLatest releaseBundled npmBundled OpenSSL
18 (Hydrogen)End of life30 Apr 202518.20.810.8.23.0.16
20 (Iron)End of life30 Apr 202620.20.210.8.23.0.19
22 (Jod)Maintenance LTS30 Apr 202722.23.310.9.93.5.8
24 (Krypton)Active LTS, maintenance from 20 Oct 202630 Apr 202824.21.011.19.03.5.8
26Current, LTS from 28 Oct 202630 Apr 202926.10.011.19.13.5.8

Release versions and bundled components come from the nodejs.org distribution index. One schedule change is worth knowing about: starting with Node.js 27, Node.js moves to one major release a year, and every major line becomes LTS after its Current phase. Node.js 26 is the last line under the old odd and even pattern.

Should you target 22 or 24?

  • Target 24 if your dependencies support it. You get support until April 2028 and you do the work once.
  • Stop at 22 only if something in your stack does not run on 24 yet, for example a native add-on with no Node.js 24 build or a TLS peer that still presents a 1024-bit RSA key. Plan the second hop now, because 22 ends in April 2027.
  • Skip 26 for now. It becomes LTS on 28 October 2026. If you are starting the upgrade in 2027, evaluate it then.

Jumping straight from 18 to 24 is fine. Read the 20 to 22 and 22 to 24 changes below together and fix everything they list.

Breaking changes that affect real applications

ChangeSinceWhat breaksWhat to do
Import assertions removed22.0.0import data from './data.json' assert { type: 'json' } fails to parseUse with { type: 'json' }. Codemod: npx codemod run @nodejs/import-assertions-to-attributes. The with form works from 18.20, so the change can ship before you switch runtimes.
crypto.createCipher() and createDecipher() removed (DEP0106)22.0.0Calls throwMove to createCipheriv(). Codemod: @nodejs/crypto-createcipheriv-migration. The migration guide warns the new code cannot decrypt data written by createCipher(), so plan a re-encryption step for stored data.
require() of ES modules enabled22.12.0, 20.19.0Packages that relied on require() failing for ESM, or dual packages, can resolve differentlyRun your test suite on the target. process.features.require_module tells you if it is on, and --no-experimental-require-module turns it off while you investigate.
util.is* helpers, util.log, util._extend, the Hash and Hmac constructors and the fs.Stats constructor runtime-deprecated22.0.0Deprecation warnings at runtime, and failures if you run with --throw-deprecationReplace with typeof, Array.isArray, Object.assign and crypto.createHash().
Default stream highWaterMark raised22.0.0Memory use per stream changesRe-measure memory on services that hold many open streams.
Global WebSocket enabled22.0.0Polyfills that assume no global can behave differentlyCheck code that tests for globalThis.WebSocket.
OpenSSL 3.5 at security level 224.x (OpenSSL 3.5 from 24.5.0)RSA, DSA and DH keys under 2048 bits, ECC keys under 224 bits and RC4 cipher suites are refusedAudit certificates and keys on both sides of every TLS connection. Node.js 22 also bundles OpenSSL 3.5 since 22.20.0, but holds the security level at 1.
url.parse() runtime-deprecated24.0.0Warnings at runtime. Node.js 20 already warned on non-numeric ports.Move to the WHATWG URL API.
tls.createSecurePair() removed24.0.0Calls throwCodemod: @nodejs/tls-create-secure-pair-to-tls-socket.
dirent.path removed, fs.truncate() with a file descriptor rejected, fs.F_OK style constants deprecated24.0.0File system helpers fail or warnCodemods: @nodejs/dirent-path-to-parent-path, @nodejs/fs-truncate-fd-deprecation, @nodejs/fs-access-mode-constants.
http.OutgoingMessage._headers and ._headersList removed, SlowBuffer runtime-deprecated24.0.0Old HTTP middleware and buffer codeUse getHeaders() and Buffer.allocUnsafeSlow(). Usually the call is in an old dependency, so upgrade it.
AsyncLocalStorage uses AsyncContextFrame by default24.0.0Tracing and APM context can be lost with old agentsUpgrade your APM agent to a release that lists Node.js 24 support.
Permission model flag renamed24.0.0--experimental-permission in start scriptsUse --permission.
npm 11 bundled24.0.0Lockfile and script behaviour can differ from npm 10Regenerate the lockfile on the target and review the diff.
Platform support23.0.0, 24.0.032-bit Windows dropped in 23, 32-bit armv7 Linux dropped in 24, macOS 13.5 minimum, Linux needs glibc 2.28 or laterCheck base images and build agents, especially CentOS 7 era hosts.
Native add-onsEvery majorModules compiled for the old ABI fail to loadRebuild. The 22 to 24 guide notes add-ons may need updates for V8 13.6 and C++20.

If you are coming from Node.js 16 or earlier, also read our post on ERR_OSSL_EVP_UNSUPPORTED. Node.js 18 already moved to OpenSSL 3.0, so that error usually shows up one upgrade earlier than this one.

Upgrade steps

  1. Inventory every place Node.js runs. Container base images, CI runners, .nvmrc and .node-version files, the engines field in package.json, serverless runtime settings and any host with a system package. Record the exact version of each.
  2. Surface deprecations on the version you run today. Run the test suite with --pending-deprecation and --throw-deprecation. Anything that fails is a call site to fix before the switch.
  3. Run the codemods. Apply the ones in the table that match your code, commit them separately and keep them compatible with the old runtime so they can ship before the upgrade.
  4. Upgrade dependencies that pin Node.js behaviour. APM agents, native add-ons (bcrypt, sharp, database drivers with C bindings), test frameworks and build tools. Check each one's supported Node.js range.
  5. Audit TLS material for Node.js 24. For each certificate your services present or trust, check the key size, for example openssl x509 -in cert.pem -noout -text | grep "Public-Key". Anything under 2048-bit RSA fails at security level 2. Include outbound peers such as payment gateways, LDAP and internal APIs.
  6. Switch the runtime in one place. Change the base image or version file, delete node_modules, reinstall so native modules rebuild, and commit the lockfile npm writes.
  7. Roll out gradually. Canary one instance or a small share of traffic, compare error rates, latency, memory and event loop lag against the old version, then widen.

Test plan

  • Full unit and integration suite on the target version, with --throw-deprecation in CI so new deprecations fail the build.
  • A TLS smoke test against every external endpoint the service calls, run on the target runtime.
  • Startup of every entry point, including workers, cron jobs and CLI scripts that tests do not cover.
  • Decryption of a sample of stored data if you replaced createCipher().
  • Load test of the busiest service to catch memory changes from stream buffering and the new V8.
  • Trace continuity in your APM tool across async boundaries.

Rollback plan

  • Keep the previous image tag or package version deployable until the canary has run through a full business cycle.
  • Ship code changes before the runtime change and keep them compatible with both versions. Then rollback is a runtime switch only.
  • Keep the old lockfile on a branch. Running the npm 11 lockfile under npm 10 during an incident is a second change you do not want.
  • Do not adopt APIs that only exist on the new line until the rollback window has closed.
  • If you migrated encrypted data to a new cipher, keep the old ciphertext until you are sure you will not roll back.

If you cannot upgrade in time

Some services cannot move quickly: a native add-on with no new build, a vendor SDK pinned to an old runtime, or a frozen application nobody wants to touch. For those, OSSeva backports Node.js security fixes, including V8 and libuv CVEs, to the 14, 16, 18 and 20 lines and ships them as signed builds, with VEX attestation so scanners and auditors can see which findings are fixed. The Operate tier adds 24/7 runtime monitoring with named Node.js engineers. See Node.js support, the Node.js vulnerabilities by version list, and the end-of-life pages for Node.js 18, Node.js 20 and Node.js 22. The full timeline is on the Node.js end-of-life tracker.

Common questions

Can I go from Node.js 18 straight to 24?

Yes. There is no requirement to stop at 20 or 22. Fix everything in both migration guides and test on 24 directly.

Is Node.js 22 still safe to run?

Yes, until 30 April 2027. It is in maintenance, so it still gets security fixes but no new features. Treat it as a short stop.

Why does a TLS connection fail on Node.js 24 but not on 22?

Both bundle OpenSSL 3.5, but Node.js 22 keeps OpenSSL at security level 1 and Node.js 24 uses the OpenSSL default of level 2. A peer with a key under 2048 bits, or one that only offers RC4, is refused on 24.

When does Node.js 26 become LTS?

On 28 October 2026, according to the release schedule. It is supported until 30 April 2029.

Tags

Node.jsUpgradeEnd of LifeOpenSSLnpm

Ready to get your open source under control?

Talk to an OSSeva engineer about CVE coverage, compliance, and migration support for your stack.