Node.js 26.0.0 shipped on 5 May 2026. The date to put in a calendar is 28 October 2026, when it becomes Active LTS, because three common defaults resolve the word "LTS" at build time rather than at commit time: node-version: lts/* in GitHub Actions, FROM node:lts in a Dockerfile, and whatever your PaaS builder calls its default runtime. Node 24 drops to Maintenance on 20 October, eight days before the flip. Two earlier cases on this site, the node20 runner removal in GitHub Actions and the Node 24.19.0 heap failures, reached people the same way, with nothing changing in the repository. Everything below comes from the 26.0.0 release notes and the pull requests linked at each claim, ordered roughly by how quietly it fails.

28 Oct 2026: Node 26 Active LTSactions/setup-node lts/*Docker FROM node:ltsPaaS builder defaultBuild runs on Node 26

The tips

  1. Pin the major now, then move it on a date you picked. actions/setup-node resolves an lts/* spec against the actions/node-versions manifest and takes the newest entry carrying an LTS codename, so it starts returning 26 as soon as a 26.x build publishes one. Point the workflow at a committed .nvmrc so the local shell and CI agree on one number, the same discipline that stops a pnpm 12 upgrade from drifting per-machine.
    - uses: actions/setup-node@v4
      with:
        node-version-file: .nvmrc   # not lts/*
  1. The Node 26 image carries neither Yarn nor Corepack, so a container build exits 127. Corepack stopped shipping with Node.js in 25.0.0, and the 26 images do not bundle Yarn 1.x either, so RUN corepack enable or RUN yarn install dies with exit 127. Installing Yarn unconditionally breaks the other direction, because npm install --global yarn fails with EEXIST on the Node 24 image that still has it. While a fleet is mid-migration I use one idempotent line that survives both bases.
    RUN command -v yarn >/dev/null 2>&1 || npm install --global yarn@1.22.22

Confirm the gap before you argue about it: docker run --rm node:26 sh -c 'command -v yarn corepack || echo missing'.

  1. Rebuild every native addon, because NODE_MODULE_VERSION is now 147. The V8 14.6.202.33 bump moved the ABI from 144 to 147, so anything compiled against Node 24 refuses to load with "was compiled against a different Node.js version using NODE_MODULE_VERSION". Put the runtime number into your prebuild cache key: a key built only from a lockfile hash will cheerfully restore Node 24 binaries into a Node 26 job, which is how you end up debugging a missing build/Release on deploy instead of in CI.
    node -p process.versions.modules   # 147 on Node 26
    npm rebuild --verbose
  1. readable.read() returns one buffer at a time, and nothing errors. PR #60441 stopped concatenating the queued buffers to avoid the copy, so code calling bare read() to drain everything buffered now gets the first chunk and continues as if that were the message. The near-equivalent is read(readable.readableLength), with a read(0) first in edge cases. Audit hand-rolled framing and any length-prefixed protocol parser, since a truncated frame looks like a peer problem rather than a runtime change.
    const all = stream.read(stream.readableLength) // was: stream.read()
  1. undici 8 moved the global dispatcher symbol, so your proxy or mock quietly stops applying. The bundled client is undici 8.0.2 and the global symbol changed, so a separately installed undici 6 or 7 calling setGlobalDispatcher() writes a key the built-in fetch no longer reads. No error, no proxy, no intercepted request, and a test suite that passes because every assertion hit the real network. Print what your build actually registers, then align the standalone dependency to the 8.x line.
    node -p "Object.getOwnPropertySymbols(globalThis).map(String).filter(s=>/undici/.test(s))"
  1. undici 8 also flips the protocol and tightens header validation. HTTP/2 is on by default (allowH2: true), so traffic moves to h2 against any server offering it over ALPN, which is awkward in front of older TLS terminators and inspecting proxies. Values that undici 6 accepted in a header now throw TypeError, which bites code assembling headers from a database or an upstream API. The 8.x line needs Node 22.19.0 or newer, so a monorepo still building on 20 cannot adopt it yet. Force h1 while you test the rest:
    import { Agent, setGlobalDispatcher } from 'undici'
    setGlobalDispatcher(new Agent({ allowH2: false }))
  1. --experimental-transform-types is removed, so enums and namespaces stop running. Plain type stripping still handles .ts files, but the transforming flag was deleted outright in 26.0.0 (PR #61803). TypeScript constructs needing emitted code, meaning enums, namespaces and decorator metadata, now require a loader such as tsx or a rewrite. A frozen object with as const replaces most enums and leaves a real runtime value behind.
  2. module.register() warns at runtime now, so migrate behind a feature detect. DEP0205 was promoted in PR #62401, and the warning points at module.registerHooks(). That replacement takes synchronous hooks only, so an async load hook needs reworking rather than renaming. Keep both paths while your minimum supported Node is below 26.
    import { register, registerHooks } from 'node:module'
    import * as hooks from './hooks.js'
    registerHooks ? registerHooks(hooks) : register('./hooks.js', import.meta.url)
  1. Turn the other three runtime deprecations into stack traces in CI. 26.0.0 promoted DEP0201, passing options.type to Duplex.toWeb() (PR #62173), plus DEP0203 and DEP0204, passing a CryptoKey to node:crypto APIs and a non-extractable one to KeyObject.from() (PR #62453). Read the changelog rather than the deprecations reference for status: as of early October 2026 that page still describes several of these as documentation-only, and the v26.x docs mis-dated at least one step, tracked in issue #63141. A bare warning names no dependency, so run one job that fails on them.
    node --trace-deprecation ./test/run.js     # stack per warning
    node --throw-deprecation ./test/run.js     # fail the job
  1. Grep node_modules, not just your own source, for the removed legacy modules. http.Server.prototype.writeHeader() reached end-of-life in favour of writeHead() (PR #60635), and the _stream_wrap, _stream_readable, _stream_writable, _stream_duplex, _stream_transform and _stream_passthrough shims are gone (PR #60657). Both show up mostly in transitive dependencies and older bundler output, where the require throws on whichever code path touches it first, often an error handler you never exercise in staging.
    grep -rlE "require\(['\"]_stream_|writeHeader\(" node_modules --include=*.js | sort -u
  1. Temporal is enabled by default, so feature-detect it instead of bundling the polyfill. Library authors shipping temporal-polyfill now push code that Node 26 consumers never execute, and mixing the two implementations causes real failures: a native ZonedDateTime is not an instanceof the polyfill class, and the two sets of TypeScript declarations can conflict in the same build. Resolve once at module load and export the winner.
    import { Temporal as polyfill } from 'temporal-polyfill'
    export const Temporal = globalThis.Temporal ?? polyfill
  1. Do not let the LTS flip push Lambda onto nodejs26.x yet. AWS announced Node.js 26 as a public preview managed runtime in August 2026, available in commercial, GovCloud and China regions at standard rates, and explicitly outside the Lambda SLA and Technical Support. A function pinned to the preview identifier graduates automatically when the runtime reaches GA, so declaring it early costs nothing; routing production invocations through it before GA does.

Wrap-up

One habit covers all twelve: nothing in your delivery path should resolve the word "LTS" at build time. Replace lts/*, node:lts and builder defaults with an explicit major, and change that major in a commit, on a weekday, with the test suite watching. ABI 147, the one-buffer read() and the undici dispatcher symbol are each an afternoon of work when you know which runtime you are on, and close to untriageable when a green pipeline switched runtimes overnight. The npm 12 rollout went the same route into production: the release arrived without passing through anyone's commit.