// OSSeva Blog
MigrationUpgrade Node.js 18 or 20 to 22 or 24: Breaking Changes, Steps and Rollback
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
| Line | Status | End of life | Latest release | Bundled npm | Bundled OpenSSL |
|---|---|---|---|---|---|
| 18 (Hydrogen) | End of life | 30 Apr 2025 | 18.20.8 | 10.8.2 | 3.0.16 |
| 20 (Iron) | End of life | 30 Apr 2026 | 20.20.2 | 10.8.2 | 3.0.19 |
| 22 (Jod) | Maintenance LTS | 30 Apr 2027 | 22.23.3 | 10.9.9 | 3.5.8 |
| 24 (Krypton) | Active LTS, maintenance from 20 Oct 2026 | 30 Apr 2028 | 24.21.0 | 11.19.0 | 3.5.8 |
| 26 | Current, LTS from 28 Oct 2026 | 30 Apr 2029 | 26.10.0 | 11.19.1 | 3.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
| Change | Since | What breaks | What to do |
|---|---|---|---|
| Import assertions removed | 22.0.0 | import data from './data.json' assert { type: 'json' } fails to parse | Use 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.0 | Calls throw | Move 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 enabled | 22.12.0, 20.19.0 | Packages that relied on require() failing for ESM, or dual packages, can resolve differently | Run 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-deprecated | 22.0.0 | Deprecation warnings at runtime, and failures if you run with --throw-deprecation | Replace with typeof, Array.isArray, Object.assign and crypto.createHash(). |
Default stream highWaterMark raised | 22.0.0 | Memory use per stream changes | Re-measure memory on services that hold many open streams. |
Global WebSocket enabled | 22.0.0 | Polyfills that assume no global can behave differently | Check code that tests for globalThis.WebSocket. |
| OpenSSL 3.5 at security level 2 | 24.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 refused | Audit 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-deprecated | 24.0.0 | Warnings at runtime. Node.js 20 already warned on non-numeric ports. | Move to the WHATWG URL API. |
tls.createSecurePair() removed | 24.0.0 | Calls throw | Codemod: @nodejs/tls-create-secure-pair-to-tls-socket. |
dirent.path removed, fs.truncate() with a file descriptor rejected, fs.F_OK style constants deprecated | 24.0.0 | File system helpers fail or warn | Codemods: @nodejs/dirent-path-to-parent-path, @nodejs/fs-truncate-fd-deprecation, @nodejs/fs-access-mode-constants. |
http.OutgoingMessage._headers and ._headersList removed, SlowBuffer runtime-deprecated | 24.0.0 | Old HTTP middleware and buffer code | Use getHeaders() and Buffer.allocUnsafeSlow(). Usually the call is in an old dependency, so upgrade it. |
AsyncLocalStorage uses AsyncContextFrame by default | 24.0.0 | Tracing and APM context can be lost with old agents | Upgrade your APM agent to a release that lists Node.js 24 support. |
| Permission model flag renamed | 24.0.0 | --experimental-permission in start scripts | Use --permission. |
| npm 11 bundled | 24.0.0 | Lockfile and script behaviour can differ from npm 10 | Regenerate the lockfile on the target and review the diff. |
| Platform support | 23.0.0, 24.0.0 | 32-bit Windows dropped in 23, 32-bit armv7 Linux dropped in 24, macOS 13.5 minimum, Linux needs glibc 2.28 or later | Check base images and build agents, especially CentOS 7 era hosts. |
| Native add-ons | Every major | Modules compiled for the old ABI fail to load | Rebuild. 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
- Inventory every place Node.js runs. Container base images, CI runners,
.nvmrcand.node-versionfiles, theenginesfield inpackage.json, serverless runtime settings and any host with a system package. Record the exact version of each. - Surface deprecations on the version you run today. Run the test suite with
--pending-deprecationand--throw-deprecation. Anything that fails is a call site to fix before the switch. - 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.
- 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.
- 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. - 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. - 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-deprecationin 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
Related articles
Ready to get your open source under control?
Talk to an OSSeva engineer about CVE coverage, compliance, and migration support for your stack.