Should you upgrade to Node.js 26 now that it's LTS?
Node 22 loses fixes on 30 April 2027, Node 24 a year later. Upgrade from 22 now, and clear Node 24's warnings first: one break is silent.
Senior Full-Stack Engineer- Published
- 9 minReading time
- EngineeringCategory
Listen to this post· AI-narrated
0:00 / 11:02On 28 October, Node.js 26 became the long-term support line, and some teams found out from a failed deploy. "Our deployment to prod failed because the person who wrote the dockerfile pinned it to LTS," one developer wrote on r/node (opens in a new tab) the next day. So should you upgrade to Node.js 26 now? It depends on the line you run. From Node 22, yes, and go straight to 26. From Node 24 you have until April 2028, but do one thing on 24 before you move, because the Node 26 change I'd worry about most doesn't throw an error. I ran the same files on Node 24 and Node 26 to see which breaks are loud and which are silent.
TL;DR: Upgrade to Node.js 26 now if you're on Node 22, which stops getting fixes on 30 April 2027. On Node 24 there's no rush, but run your tests with --throw-deprecation first: on 26, fs.W_OK is gone and a write check on a read-only file passes silently. If your image uses node:lts, your next fresh build is already on 26.
Is Node.js 26 LTS, and how long do Node 22 and 24 have?
Node.js 26 has been the Active LTS line since 28 October 2026, released as 26.13.0 'Lithium', the same code as 26.12.1 with the LTS label switched on (Node.js (opens in a new tab)). The project's release schedule (opens in a new tab) sets the clock for everything else:
| Line | Status now | End of life |
|---|---|---|
| Node 22 | Maintenance since 21 October 2025 | 30 April 2027 |
| Node 24 | Maintenance since 20 October 2026 | 30 April 2028 |
| Node 26 | Active LTS until 20 October 2027 | 30 April 2029 |
Maintenance means security and critical fixes only. Node's own releases page (opens in a new tab) says "Production applications should only use Active LTS or Maintenance LTS releases", so all three are fine to run today. What differs is runway. Counting from 4 November, Node 22 has 177 days left, and a major upgrade on a production API eats a good part of that in dependency checks, staging and a quiet deploy window. Waiting for Node 27 doesn't buy anything either. It's the first line on the project's new yearly schedule (opens in a new tab), where every release becomes LTS, but that only happens in October 2027.
When should you upgrade from Node 22 or Node 24?
On Node 22, plan the move now and go straight to 26, once your framework and database driver list 26 as supported. Stopping at 24 buys one more year of support, 26 buys two, and you only pay for one round of testing. On Node 24 you're covered until April 2028, so schedule the upgrade for the first half of 2027 and use the months before it to clear deprecation warnings on 24 (more on why below). A new project should start on 26, with Temporal, the newer V8 and the longest support window from day one.
None of this is urgent the way a CVE is. The mistake I'd avoid is drifting into Node 22's last month of support with a major upgrade still ahead of you.
Did your Docker image already upgrade to Node 26?
If your Dockerfile says FROM node:lts or node:lts-alpine, it did, on 28 October. I checked Docker Hub on 3 November: node:lts and node:26 point to the same image digest, and so do node:lts-alpine and node:26-alpine, with the lts tag updated at 23:43 UTC on 28 October (Docker Hub (opens in a new tab)). The next build that pulled a fresh base image got Node 26 with no change in your repo; a build server with the old image cached can hide the switch until the cache clears.
That's how the r/node deploy broke. The fix most commenters landed on is to pin the major version (FROM node:24-alpine) and let Renovate or Dependabot open a pull request when you want to move, so the jump goes through CI like any other change. An lts tag is fine on a laptop, but in a production image it's an upgrade you didn't schedule.
Which Node.js 26 change fails without an error?
The removal of fs.R_OK, fs.W_OK and fs.X_OK is the one that stays quiet. Node 24 deprecated these shortcuts at runtime and Node 25 removed them; the replacement is fs.constants.W_OK (Node.js deprecations, DEP0176 (opens in a new tab)). Removed here means undefined, and fs.accessSync(file, undefined) only checks that the file exists.
I ran this on the official Node 24.22.2 and 26.13.0 binaries, on macOS as a normal user. Run it the same way: root skips file permission checks (capabilities(7) (opens in a new tab), CAP_DAC_OVERRIDE), so in a container running as root EACCES never fires on either version.
const fs = require("node:fs")
const file = "locked.txt"
fs.writeFileSync(file, "read only")
fs.chmodSync(file, 0o444)
console.log("fs.W_OK is", fs.W_OK)
try {
fs.accessSync(file, fs.W_OK)
console.log("accessSync says: writable")
} catch (error) {
console.log("accessSync says: not writable,", error.code)
}
Node 24 prints fs.W_OK is 2, throws EACCES and logs a DEP0176 deprecation warning. Node 26 prints fs.W_OK is undefined and accessSync says: writable, about a file it can't write, with no warning at all. Code that checks an upload directory or a cache folder this way now says yes to every path that exists.
One reply in the same r/node thread said "at least it failed on the build instead of silently booting with breaking runtime changes in prod." The fs constants are the silent kind, and their only signal was the warning, which lives in Node 24. So before you move, run your test suite on Node 24 with NODE_OPTIONS=--throw-deprecation, which turns every deprecation your tests exercise into a failure; when one comes from a dependency, --trace-deprecation shows where. Once you're on 26 there's nothing left to throw. Node 22 never prints this warning, because the deprecation was documentation-only before Node 24, so on 22 run the suite once on Node 24 with the same flag, even if you'll deploy 26. A text search for fs.W_OK misses const { W_OK } = require("node:fs"). For the fix, the project ships a codemod: npx codemod@latest @nodejs/fs-access-mode-constants.
What else breaks when you upgrade to Node.js 26?
Most of the rest fails loudly, at install or on first run, which makes it cheaper to find. The localStorage change at the end is the other quiet one. From the Node 26.0.0 release notes (opens in a new tab) and the Node 25 changelog (opens in a new tab), the ones I'd check first:
- The
--experimental-transform-typesflag is gone. Node still strips TypeScript types, but enums need code generation. My enum file fails on both versions without the flag (ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX); with it, Node 24 prints the value and Node 26 answersbad option. Replace enums withas constobjects, or run throughtsxor a compiler. - Corepack no longer ships with Node (since 25). A
corepack enableline in CI or a Dockerfile fails until you install Corepack from npm or install your package manager directly. - Native addons need a rebuild:
NODE_MODULE_VERSIONmoved to 147. Addons built on Node-API keep working; anything compiled against V8 directly needs a clean install on 26. - Since Node 25 the official Linux binaries link against libatomic (Node.js (opens in a new tab)). The official Docker images include it; a distroless image or a hand-unpacked tarball may not, and
nodeexits before your code runs. response.writeHeader()and the internal_stream_*modules are gone, and old dependencies are the usual culprits.localStorageexists on the server:'localStorage' in globalThisis nowtrueon 26 (it wasfalseon 24), while its value staysundefinedwithout--localstorage-file. Browser detection written that way takes the wrong branch.
module.register() still works but warns on every run; the replacement is module.registerHooks(). Test runners and loaders are where you'll see it, and updating the tool usually clears it.
What do you get for upgrading to Node.js 26?
The main addition is Temporal, the date and time API meant to replace Date, now on by default: typeof Temporal is "object" on Node 26 and "undefined" on Node 24 in my run. It handles time zones, plain calendar dates and durations without a library. V8 moves to 14.6, which adds Map.prototype.getOrInsert(), getOrInsertComputed() and Iterator.concat(), and the built-in fetch() now runs on Undici 8 (Node.js (opens in a new tab)). That last one cuts both ways for an API: if your service calls other services with fetch(), its HTTP client just moved a major version, so run those integration tests before anything else.
Don't rewrite working date code just because Temporal is there. Use it in new code, and keep date-fns or luxon wherever the same code still has to run on Node 24.
What to do this week
Run node -v on production and check every place the version is set: .nvmrc, CI, the Dockerfile, your host's settings and engines in package.json. If the Dockerfile or CI says lts, or engines is an open range like >=22, pin the major. Then run the tests on Node 24 with NODE_OPTIONS=--throw-deprecation and fix what fails, whether you're on 22 or 24 today. On Node 22, put the move to 26 on this quarter's plan. On Node 24, put it on next year's, after the warnings are clean.
I write about upgrades, stacks and building with agents on the gabs.app blog. I'm a full-stack engineer and my work is on gabs.app; if you're hiring, the contact form comes straight to me.