Node 26 Becomes LTS on 28 October: Upgrade Guide from 24 and 22
Node.js 26 is scheduled to enter Long Term Support on 28 October 2026, eight days after Node 24 drops from Active LTS to Maintenance on 20 October, and it is supported through 30 April 2029.[[1]](#cite-1) Node 22 stays in Maintenance until 30 April 2027, and Node 20 has been end-of-life since 30 April 2026. As of 19 October the latest 26.x release is 26.12.0, shipped 14 October with V8 14.6 and npm 11.20.0[[2]](#cite-2). This post gives you the full schedule table from the official file, the changes in Node 26 that can actually break an upgrade, the exact commands for nvm, fnm, Volta, GitHub Actions and Docker, and a plain answer to whether you should move now or wait. Nothing here is true until the release team flips the switch, so every date below is the planned one.
TL;DR
- Node 26 becomes LTS on 28 October 2026 (planned). The schedule file lists no codename yet, so none is stated here.
- Node 24 enters Maintenance on 20 October 2026 and reaches end-of-life on 30 April 2028. Staying on 24 is safe for production.
- Node 22 is already in Maintenance and ends on 30 April 2027. It is the line that needs a plan first.
- Node 20 is end-of-life since 30 April 2026 and gets no security fixes.
- What bites in 26: removed http.Server.prototype.writeHeader(), removed legacy _stream_* modules, runtime deprecations for module.register(), and the removal of --experimental-transform-types.
- Run your tests on 26 with --trace-deprecation this week; switch production after 28 October, not before.
Node.js release schedule as of 19 October 2026
The Node.js Release working group publishes its schedule as a JSON file, and the README table in the same repository is generated from the same dates[[1]](#cite-1)[[3]](#cite-3). The table below is copied from that file. The README adds the caveat that dates are subject to change, and that the exact date of an LTS move is chosen no later than the first day of the month it happens in.
| Line | Status on 19 Oct 2026 | First release | LTS start | Maintenance start | End-of-life |
| --- | --- | --- | --- | --- | --- |
| Node 20 (Iron) | End-of-life | 2023-04-18 | 2023-10-24 | 2024-10-22 | 2026-04-30 |
| Node 22 (Jod) | Maintenance LTS | 2024-04-24 | 2024-10-29 | 2025-10-21 | 2027-04-30 |
| Node 24 (Krypton) | Active LTS until 20 Oct | 2025-05-06 | 2025-10-28 | 2026-10-20 | 2028-04-30 |
| Node 25 | End-of-life | 2025-10-15 | none (odd line) | none | 2026-06-01 |
| Node 26 | Current | 2026-05-05 | 2026-10-28 | 2027-10-20 | 2029-04-30 |
| Node 27 | Not released | alpha 2026-10-28, start 2027-04-22 | not listed | 2027-10-20 | 2030-04-30 |
Two readings of that table matter. First, the working group says an even-numbered line stays in Active LTS for 12 months and then spends 18 months in Maintenance[[3]](#cite-3). Node 24 and 26 both fit that pattern: 28 October to 20 October is the 12 months, 20 October to 30 April the 18. Second, the Node 27 row carries no LTS date and no codename in the schedule file. The file does not say what that means for the odd/even rule, so check the file again before you plan anything around 27.
What Maintenance means for Node 24
The README defines Maintenance as critical bug fixes and security updates, with new features added only at the release team's discretion, typically when they help migration to a later line[[3]](#cite-3). It does not mean abandoned. Node 24 will keep getting security releases for another 18 months. The practical difference is that you should stop expecting backports of new APIs, and you should treat the line as a place you are leaving, not a place you are settling.
Why Node 22 is the line that needs a decision
Node 22 has about six months of life left (30 April 2027). If you run it, the question is not whether to move but whether to go to 24 or straight to 26. Going to 24 buys you until April 2028. Going to 26 buys you until April 2029 and one fewer migration. The catch is that 26 has been out for five months as a Current release, and the changes in the next section apply to you in full.
What changed in Node 26 that matters when you upgrade
The 26.0.0 release notes, dated 5 May 2026, list the headline items as the Temporal API enabled by default, V8 updated to 14.6 and Undici updated to 8.0[[4]](#cite-4). Version numbers from the release feed: 26.12.0 ships V8 14.6.202.34 and npm 11.20.0, while Node 24.22.0 ships V8 13.6 and npm 11.19.1, and Node 22.23.3 ships V8 12.4 and npm 10.9.9[[2]](#cite-2). The npm major jump matters if you move from 22, because npm 10 to 11 is a separate set of changes to read.
New in 26.0.0
- Temporal API on by default. The modern date and time API is available without a flag, contributed by Richard Lau in PR 61806[[4]](#cite-4). Your Date code keeps working. Nothing forces you to migrate, but libraries that feature-detect Temporal may change behavior once it exists.
- V8 14.6. The notes call out Map.prototype.getOrInsert() and getOrInsertComputed() (also on WeakMap) plus Iterator.concat()[[4]](#cite-4). Both are small, but getOrInsertComputed removes the common get-check-set pattern in caches.
- Undici 8.0.2 in the 26.0.0 release, which backs fetch. If you pin a custom undici dispatcher or agent, retest it.
Removed and deprecated in 26.0.0
These are the entries that break builds, taken from the Deprecations and Removals section of the changelog[[4]](#cite-4):
- http.Server.prototype.writeHeader() is gone. Use writeHead().
- The legacy _stream_wrap, _stream_readable, _stream_writable, _stream_duplex, _stream_transform and _stream_passthrough modules are removed. Old packages that require('_stream_readable') will throw. Import from node:stream.
- --experimental-transform-types is removed, which hits anyone running TypeScript files through that flag.
- module.register() is now runtime-deprecated, so loader setups built on it print a warning.
- DEP0182 moved to end-of-life, DEP0203 and DEP0204 became runtime deprecations in crypto, and DEP0201 in stream became a runtime deprecation. I did not look up what each DEP number covers in the deprecations page, so grep your warnings for those codes.
- Platform floor: the build now needs GCC 13.2 and drops Python 3.9[[4]](#cite-4). That matters if you compile Node yourself or build native addons on old CI images.
One more change that is easy to miss: localStorage now returns undefined when no storage file is configured, per the semver-major list in the same release[[4]](#cite-4). Code that assumed an object will fail at the first property access.
Useful additions since 26.0.0
Node 26 already has twelve minor releases (26.1.0 to 26.12.0), so some features you may want are not in 24. These are the ones listed under Notable Changes, with the release that added them[[4]](#cite-4):
| Version | Date | Addition |
| --- | --- | --- |
| 26.1.0 | 2026-05-07 | Experimental node:ffi module, behind --experimental-ffi |
| 26.2.0 | 2026-05-20 | stream.compose marked stable; writeInformation for 1xx responses |
| 26.8.0 | 2026-08-26 | TracingChannel in diagnostics_channel marked stable |
| 26.9.0 | 2026-09-16 | node:ffi enabled by default |
| 26.11.0 | 2026-10-07 | Buffer.stringLength(), isLatin1, Alpine Linux promoted to tier 2 support |
| 26.12.0 | 2026-10-14 | http.websocketMask() and websocketUnmask() |
The ffi row deserves a second look. The 26.1.0 notes say the API is inherently unsafe, since bad pointers or wrong signatures can crash the process or corrupt memory[[4]](#cite-4). Being on by default in 26.9.0 does not make it safe. Treat it as something you do not use in production code until it has a track record. Also from the notes: the 26.3.0 release flags a risk that macOS Universal Binaries may not be maintained for the full life of Node 26, though the stated intention is to keep shipping them as long as practical.
Upgrade checklist: Node 24 or 22 to Node 26
1. Install 26 beside your current version
Do not replace anything yet. Install 26 next to what you run, so you can switch per shell.
# nvm: install, then carry global packages over from 24
nvm install --reinstall-packages-from=24 26
nvm use 26
# fnm
fnm install 26
fnm use 26
# Volta (pins the version in package.json)
volta install node@26
volta pin node@26
The --reinstall-packages-from flag is documented in the nvm README[[5]](#cite-5), and fnm install [VERSION] and fnm use [VERSION] are in the fnm command reference[[6]](#cite-6). The Volta commands are from memory of its docs and I did not re-verify them today, so run volta --help if one fails. After 28 October, nvm install --lts should resolve to 26, but check node -v rather than trusting it.
2. Declare what you support in package.json
{
"engines": {
"node": ">=24 <27"
}
}
Widening engines to cover both 24 and 26 lets you run both in CI during the transition without npm complaining. Add "engine-strict=true" to .npmrc if you want npm to enforce it. Also drop a .nvmrc or .node-version file: the actions/setup-node action reads either through its node-version-file input[[7]](#cite-7). If you use Corepack for package managers, run corepack --version after installing 26 to confirm it is present, because Corepack distribution has changed across recent Node releases and I did not trace the details.
3. Run a CI matrix on both lines
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node: [24, 26]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v7
with:
node-version: ${{ matrix.node }}
- run: npm ci
- run: npm test
The current major of actions/setup-node is v7; its latest release, v7.1.0, was published on 8 October 2026[[7]](#cite-7). The README also notes that automatic package-manager caching is on by default and can be turned off with package-manager-cache: false for workflows that handle sensitive data. For a full pipeline around this step, see our [GitHub Actions CI/CD pipeline for Node.js](/blogs/github-actions-ci-cd-nodejs-complete-pipeline-2026).
4. Update Docker base images
Docker Hub already carries the 26 tags: node:26, node:26-slim, node:26-alpine, node:26-bookworm-slim and node:26-trixie-slim, plus pinned forms like node:26.12.0[[8]](#cite-8). Pin a major (node:26-slim) in production, and pin the full version if you need byte-identical rebuilds. I could not confirm from a primary source whether Docker will add an lts alias pointing at 26 on 28 October, so do not depend on one. If your image builds native addons on Alpine, remember that 26.11.0 promoted Alpine to tier 2 support[[4]](#cite-4), which is good news but not a reason to skip a test build. Our [production Docker Compose guide for Node.js](/blogs/docker-compose-production-nodejs-redis-postgres-2026) covers the rest of the stack.
How to test before you switch
1. Run the full suite on 26. Start with unit tests, then integration tests against real databases and queues. Failures from removed APIs show up as thrown errors, so they are loud.
2. Surface deprecations. Run node --trace-deprecation your-entry.js or set NODE_OPTIONS=--trace-deprecation in CI. Runtime deprecations print a warning with a stack, and the stack usually points at a dependency, not your code. To make the suite fail on them, use --throw-deprecation.
3. Grep for the removed names. Search your source and node_modules for writeHeader(, _stream_, module.register and experimental-transform-types. Dependencies are the usual source of old stream imports.
4. Rebuild native addons. Delete node_modules and run npm ci on 26 so every addon compiles against the new V8. Anything built with node-gyp needs a toolchain that supports GCC 13.2 on Linux; addons that ship prebuilt binaries need a release that targets Node 26's ABI. If a package has no 26 build, that is your blocker, not Node.
5. Diff your fixtures and configs. When a test fails on a data shape, normalize the sample before comparing. Paste it into the [JSON formatter](/tools/json-formatter) to normalize them before comparing, or run YAML configs through the [YAML to JSON converter](/tools/yaml-to-json-converter).
6. Soak in staging. Run 26 under real traffic in staging for a week and watch memory and latency. A new V8 changes garbage collection behavior in ways unit tests do not show.
If your project is a command-line tool, our [Node.js and TypeScript CLI guide](/blogs/build-cli-tool-nodejs-typescript-complete-guide-2026) has patterns for testing across several runtime versions.
When to upgrade now, and when to wait
| Your situation | Recommendation | Reason |
| --- | --- | --- |
| Production on Node 20 | Move now, to 24 at minimum | End-of-life since 30 April 2026; no security fixes |
| Production on Node 22 | Plan the move before spring 2027; test on 26 now | Ends 30 April 2027 |
| Production on Node 24 | Wait. Test on 26, switch in 2027 | Supported until 30 April 2028 |
| New project | Start on 24 today, or 26 after 28 October | 26 is the longer runway (to April 2029) |
| Library author | Add 26 to your CI matrix now | Users will adopt 26 as soon as it is LTS |
The reason to wait on 24 is boring and sound: LTS lines get fixes that have been audited by the release team, and a Current line that has just changed status has had less production exposure than the fifth month of a stable one. Node 26 has had five months of Current releases, and the 26.3.1 and 26.5.1 releases each shipped a list of CVE fixes[[4]](#cite-4). Active patching counts in its favor, though it also shows the line has had real security work to do.
If you do move before the LTS date, treat it as a normal Current-line adoption: pin an exact version, watch the changelog for each minor, and be ready to roll back.
Frequently asked questions
What date does Node 26 become LTS?
The official schedule file lists 28 October 2026 as the LTS start for Node 26[[1]](#cite-1). The README says dates are subject to change, so confirm on the day. This post was written on 19 October, before that date.
Is Node 24 still safe to use after 20 October?
Yes. From 20 October 2026 it is in Maintenance, which means critical bug fixes and security updates, until 30 April 2028[[1]](#cite-1). You lose new-feature backports, not security fixes.
When does Node 22 reach end-of-life?
30 April 2027, according to the schedule file[[1]](#cite-1). It has been in Maintenance since 21 October 2025.
Is Node 20 end-of-life already?
Yes. The schedule lists 30 April 2026 as its end date[[1]](#cite-1). If you still run it, upgrade to 24 or 26 rather than hunting for patched builds.
Does Node 26 have a codename?
Not in the schedule file as of 19 October 2026, where the codename field for v26 is empty[[1]](#cite-1). Earlier LTS lines got element names such as Jod and Krypton, but I will not guess one for 26.
Can I skip Node 24 and go from 22 straight to 26?
Yes, and many teams will. You inherit every removal and deprecation between the two lines, plus the npm 10 to 11 change, so read the changelogs for 24 and 26 and run the test steps above. I only reviewed the 26 changelog for this post.
Sources
1. nodejs/Release: schedule.json (read 19 October 2026)
2. Node.js release feed, nodejs.org/dist/index.json (read 19 October 2026)
3. nodejs/Release README: release schedule and phases (read 19 October 2026)
4. Node.js 26 changelog, CHANGELOG_V26.md (read 19 October 2026)
5. nvm README: installing and --reinstall-packages-from
6. fnm command reference
7. actions/setup-node README and releases (v7.1.0, 8 October 2026)
8. Docker Hub: official node image tags (read 19 October 2026)
---
Originally published at wowhow.cloud
Mentions
volta ってもうメンテされていないのか⋯。 fnm に移行するか~。 schniz/fnm
Node.jsとpnpmのバージョンをプロジェクトごとに固定するベストプラクティス(2026年版) はじめに Node.js と pnpm を複数のプロジェクトで使っていると、次のようなことが起きます。 手元の Node.js は 26、CI は 24、同僚は 22。誰の環境でだけ壊れるのかわからない pnpm のメジャーバージョンが違うと pnpm-lock.yaml の書式が変わり、差分が荒れる 「とりあえず npm i -g pnpm」したバージョンが人によってバラバラ 対策は「Node.js と pnpm のバージョンをリポジトリに書いてコミットし、ローカルも CI もそれに従わせる」ことに尽きます。この記事では、そのための構成を設定ファイルの実例つきでまとめます。 ネットでよく見かける構成(Corepack を有効にする、.npmrc に engine-strict=true を書く)は、Node.js 25 と pnpm 11 以降ではそのままでは動かなくなっています。この記事は次のバージョンを前提にしています(2026年10月時点)。 ツール バージョン Node.js 24 系(Active LTS)/ 26 系(Current) pnpm 12.14 actions/setup-node v7 pnpm/action-setup v6 pnpm/setup v3 構成の全体像 領域 推奨 役割 Node.js のバージョン .node-version + mise または fnm ディレクトリに入るとバージョンが自動で切り替わる pnpm のバージョン package.json の packageManager pnpm 自身が指定のバージョンに切り替わって動く 不一致の検出 engines.node + pnpm-workspace.yaml の engineStrict: true 要件を満たさない Node.js での pnpm install をエラーにする CI pnpm/setup または pnpm/action-setup + actions/setup-node 上の設定ファイルを読んでローカルと同じバージョンを使う 更新 Renovate .node-version と packageManager の更新 PR を自動で作る バージョンの「正」はリポジトリ内のファイルだけにして、ローカルの仕組み・CI・Renovate がそれを読む、という形です。 ① Node.js のバージョンを固定する(.node-version) プロジェクトのルートに .node-version を置き、Git にコミットします。 .node-version 24.22.0 .nvmrc でも構いませんが、.node-version は mise / fnm / nodenv / actions/setup-node / pnpm/setup / Renovate のどれもが読める、いちばん互換性の高い書き方です。 パッチバージョンまで書くか 24 のようにメジャーだけ書くかは好みですが、チームで揃えるならパッチまで書き、更新は Renovate に任せるのがおすすめです。メジャーだけだと、CI がいつの間にか新しいパッチで動いていた、ということが起きます。 ローカルで自動的に切り替える .node-version を読んでディレクトリごとに Node.js を切り替えるツールとして、mise か fnm を使います。 mise の場合 mise は Node.js 以外(Python、Terraform など)もまとめて管理できるバージョンマネージャーです。 brew install mise echo 'eval "$(mise activate zsh)"' >> ~/.zshrc mise は既定では .node-version を読みません(mise 自身の mise.toml を読みます)。.node-version を読ませるには、グローバル設定で有効にします。 ~/.config/mise/config.toml [settings] idiomatic_version_file_enable_tools = ["node"] これで、.node-version のあるディレクトリに cd すると、そのバージョンが使われます。未インストールなら mise install で入ります。 fnm の場合 fnm は Rust 製の Node.js 専用バージョンマネージャーです。Node.js だけで十分ならこちらが軽量です。 brew install fnm echo 'eval "$(fnm env --use-on-cd --shell zsh)"' >> ~/.zshrc --use-on-cd を付けると、cd のたびに .node-version(または .nvmrc)を読んでバージョンを切り替えます。 ② pnpm のバージョンを固定する(packageManager) package.json の packageManager フィールドに pnpm のバージョンを書きます。 package.json { "name": "my-project", "private": true, "packageManager": "pnpm@12.14.0", "engines": { "node": ">=24" } } pnpm 自身がバージョンを切り替える pnpm 11 以降では、pnpm 自身が packageManager を読み、書かれたバージョンをダウンロードしてそれで動き直します。グローバルに入っている pnpm が 12 でも、packageManager が pnpm@11.28.6 なら 11.28.6 で動きます。 $ pnpm -v # グローバルの pnpm は 12.14.0 11.28.6 # packageManager に従って 11.28.6 で動いている そのため、pnpm はグローバルに1つ入れておくだけで済みます。 curl -fsSL get.pnpm.io | sh - pnpm 12 は Node.js に依存しない単体のバイナリなので、Node.js のバージョンを切り替えても pnpm が消えたり動かなくなったりしません。 この動きは pmOnFail 設定で変えられます。 値 動作 download(既定) 指定のバージョンをダウンロードして実行 error バージョンが違えばエラー warn 警告を出してそのまま実行 ignore チェックしない(mise / Volta などに任せる場合) ! pnpm 10 までの managePackageManagerVersions / packageManagerStrict / packageManagerStrictVersion は pnpm 11 で削除され、pmOnFail に統合されました。 バージョンを上げる プロジェクトのディレクトリで pnpm self-update を実行すると、グローバルの pnpm ではなく packageManager の値が書き換わります(pnpm 11.10 以降)。 $ pnpm self-update 12.14.0 The current project has been updated to use pnpm v12.14.0 $ grep packageManager package.json "packageManager": "pnpm@12.14.0", Corepack は必須ではなくなった 以前は「corepack enable pnpm して corepack use pnpm@latest」が定番でしたが、事情が変わっています。 Node.js 25 以降には Corepack が同梱されていません。使うなら npm install -g corepack で別途入れる必要があります pnpm 自身が packageManager に従ってくれるので、Corepack がなくても同じことができます Node.js 24 以前で Corepack をすでに使っているプロジェクトはそのままでも動きますが、新しく構成するなら pnpm 自身に任せるほうがシンプルです。 ③ バージョン不一致をエラーにする(engineStrict) engines.node を書いただけでは、要件を満たさない Node.js でも pnpm install は通ってしまいます(pnpm 12.14 で確認)。エラーにするには engineStrict を有効にします。 pnpm 11 以降では、.npmrc から読まれるのは認証とレジストリの設定だけです。engine-strict=true を .npmrc に書いても無視されるので、pnpm-workspace.yaml に書きます(モノレポでなくても、pnpm の設定ファイルとして使えます)。 pnpm-workspace.yaml engineStrict: true Node.js 24.12 で engines.node: ">=26" のプロジェクトを pnpm install した結果です。 $ pnpm install Error: ERR_PNPM_UNSUPPORTED_ENGINE × Unsupported engine for /Users/gekal/work/my-project: wanted: │ {"node":">=26"} (current: {"node":"24.12.0"}) pnpm 書く場所 書き方 10 まで .npmrc engine-strict=true 11 以降 pnpm-workspace.yaml engineStrict: true engines.node には .node-version と矛盾しない範囲(例: .node-version が 24.22.0 なら >=24)を書きます。engineStrict を有効にすると、依存パッケージの engines も厳密にチェックされる点には注意してください。 ④ CI(GitHub Actions) CI では、ここまでの設定ファイルを読ませてバージョンを指定しないのがポイントです。バージョンを YAML に直書きすると、ローカルとずれる原因になります。 pnpm/setup を使う(シンプル) pnpm/setup は、pnpm と Node.js を1ステップで入れられるアクションです。packageManager から pnpm のバージョンを、.node-version から Node.js のバージョンを読み、pnpm install まで実行します。 .github/workflows/ci.yml name: CI on: push: branches: [main] pull_request: jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 packageManager と .node-version を読んで pnpm と Node.js を入れ、pnpm install まで行う - uses: pnpm/setup@v3 with: cache: true require-lockfile: true # pnpm-lock.yaml がなければ失敗させる(--frozen-lockfile) - run: pnpm build - run: pnpm test actions/setup-node と組み合わせる(従来型) actions/setup-node を使い続けたい場合は、pnpm/action-setup と組み合わせます。cache: pnpm を使うので、pnpm を先に入れる必要があります。 .github/workflows/ci.yml steps: - uses: actions/checkout@v7 version を省略すると packageManager から読む - uses: pnpm/action-setup@v6 - uses: actions/setup-node@v7 with: node-version-file: .node-version cache: pnpm - run: pnpm install --frozen-lockfile - run: pnpm build actions/setup-node v6 以降は、pnpm のキャッシュが自動では有効になりません。cache: pnpm を明示します。 ⑤ 更新を自動化する(Renovate) バージョンを固定すると、今度は「上げ忘れ」が問題になります。Renovate を入れておくと、.node-version と packageManager の更新 PR を自動で作ってくれます。 renovate.json { "$schema": "docs.renovatebot.com", "extends": ["config:recommended"], "packageRules": [ { "matchDepNames": ["node"], "allowedVersions": "/^\d*[02468]\./", "description": "Node.js は偶数(LTS になる)メジャーだけを対象にする" } ] } allowedVersions で奇数メジャー(25 など、LTS にならない Current 版)を除外しています。CI が通れば PR をマージするだけで、ローカル(mise / fnm)・pnpm・CI のバージョンが揃って上がります。 発展: pnpm に Node.js の管理も任せる pnpm 10.14 以降では、package.json の devEngines.runtime に Node.js のバージョンを書くと、pnpm が Node.js 自体をダウンロードして、スクリプトをそのバージョンで実行します。 package.json { "packageManager": "pnpm@12.14.0", "devEngines": { "runtime": { "name": "node", "version": "^26.0.0", "onFail": "download" } }, "scripts": { "v": "node -v" } } $ node -v v24.12.0 $ pnpm install devDependencies: - node 26.12.0 $ pnpm run v v26.12.0 解決された正確なバージョンとチェックサムは pnpm-lock.yaml に記録されます。CI でも pnpm/setup が devEngines.runtime を読んで同じ Node.js を入れてくれます。 mise や fnm がいらなくなるので魅力的ですが、pnpm run を通さずに直接 node を叩いた場合や、エディタの TypeScript サーバー・デバッガーが使う Node.js は別に考える必要があります。現時点では、.node-version + mise / fnm を基本にし、pnpm にまとめるのは試してみて問題なければ、くらいの位置付けがよいと思います。 まとめ ファイル 内容 .node-version 24.22.0 のように Node.js のバージョンを書く package.json packageManager: "pnpm@12.14.0" と engines.node pnpm-workspace.yaml engineStrict: true(pnpm 11 以降。.npmrc では効かない) .github/workflows/ci.yml バージョンは書かず、上のファイルを読ませる renovate.json .node-version と packageManager を自動更新 Node.js のバージョンは .node-version に書き、mise / fnm で自動的に切り替える pnpm のバージョンは packageManager に書く。pnpm 11 以降は pnpm 自身が切り替えるので、Corepack は不要(Node.js 25 以降は同梱もされていない) 不一致をエラーにするなら、pnpm-workspace.yaml に engineStrict: true CI はファイルを読ませるだけにして、YAML にバージョンを直書きしない 上げ忘れは Renovate に任せる
Node 26 Becomes LTS on 28 October: Upgrade Guide from 24 and 22 Node.js 26 is scheduled to enter Long Term Support on 28 October 2026, eight days after Node 24 drops from Active LTS to Maintenance on 20 October, and it is supported through 30 April 2029.[[1]](#cite-1) Node 22 stays in Maintenance until 30 April 2027, and Node 20 has been end-of-life since 30 April 2026. As of 19 October the latest 26.x release is 26.12.0, shipped 14 October with V8 14.6 and npm 11.20.0[[2]](#cite-2). This post gives you the full schedule table from the official file, the changes in Node 26 that can actually break an upgrade, the exact commands for nvm, fnm, Volta, GitHub Actions and Docker, and a plain answer to whether you should move now or wait. Nothing here is true until the release team flips the switch, so every date below is the planned one. TL;DR - Node 26 becomes LTS on 28 October 2026 (planned). The schedule file lists no codename yet, so none is stated here. - Node 24 enters Maintenance on 20 October 2026 and reaches end-of-life on 30 April 2028. Staying on 24 is safe for production. - Node 22 is already in Maintenance and ends on 30 April 2027. It is the line that needs a plan first. - Node 20 is end-of-life since 30 April 2026 and gets no security fixes. - What bites in 26: removed http.Server.prototype.writeHeader(), removed legacy _stream_* modules, runtime deprecations for module.register(), and the removal of --experimental-transform-types. - Run your tests on 26 with --trace-deprecation this week; switch production after 28 October, not before. Node.js release schedule as of 19 October 2026 The Node.js Release working group publishes its schedule as a JSON file, and the README table in the same repository is generated from the same dates[[1]](#cite-1)[[3]](#cite-3). The table below is copied from that file. The README adds the caveat that dates are subject to change, and that the exact date of an LTS move is chosen no later than the first day of the month it happens in. | Line | Status on 19 Oct 2026 | First release | LTS start | Maintenance start | End-of-life | | --- | --- | --- | --- | --- | --- | | Node 20 (Iron) | End-of-life | 2023-04-18 | 2023-10-24 | 2024-10-22 | 2026-04-30 | | Node 22 (Jod) | Maintenance LTS | 2024-04-24 | 2024-10-29 | 2025-10-21 | 2027-04-30 | | Node 24 (Krypton) | Active LTS until 20 Oct | 2025-05-06 | 2025-10-28 | 2026-10-20 | 2028-04-30 | | Node 25 | End-of-life | 2025-10-15 | none (odd line) | none | 2026-06-01 | | Node 26 | Current | 2026-05-05 | 2026-10-28 | 2027-10-20 | 2029-04-30 | | Node 27 | Not released | alpha 2026-10-28, start 2027-04-22 | not listed | 2027-10-20 | 2030-04-30 | Two readings of that table matter. First, the working group says an even-numbered line stays in Active LTS for 12 months and then spends 18 months in Maintenance[[3]](#cite-3). Node 24 and 26 both fit that pattern: 28 October to 20 October is the 12 months, 20 October to 30 April the 18. Second, the Node 27 row carries no LTS date and no codename in the schedule file. The file does not say what that means for the odd/even rule, so check the file again before you plan anything around 27. What Maintenance means for Node 24 The README defines Maintenance as critical bug fixes and security updates, with new features added only at the release team's discretion, typically when they help migration to a later line[[3]](#cite-3). It does not mean abandoned. Node 24 will keep getting security releases for another 18 months. The practical difference is that you should stop expecting backports of new APIs, and you should treat the line as a place you are leaving, not a place you are settling. Why Node 22 is the line that needs a decision Node 22 has about six months of life left (30 April 2027). If you run it, the question is not whether to move but whether to go to 24 or straight to 26. Going to 24 buys you until April 2028. Going to 26 buys you until April 2029 and one fewer migration. The catch is that 26 has been out for five months as a Current release, and the changes in the next section apply to you in full. What changed in Node 26 that matters when you upgrade The 26.0.0 release notes, dated 5 May 2026, list the headline items as the Temporal API enabled by default, V8 updated to 14.6 and Undici updated to 8.0[[4]](#cite-4). Version numbers from the release feed: 26.12.0 ships V8 14.6.202.34 and npm 11.20.0, while Node 24.22.0 ships V8 13.6 and npm 11.19.1, and Node 22.23.3 ships V8 12.4 and npm 10.9.9[[2]](#cite-2). The npm major jump matters if you move from 22, because npm 10 to 11 is a separate set of changes to read. New in 26.0.0 - Temporal API on by default. The modern date and time API is available without a flag, contributed by Richard Lau in PR 61806[[4]](#cite-4). Your Date code keeps working. Nothing forces you to migrate, but libraries that feature-detect Temporal may change behavior once it exists. - V8 14.6. The notes call out Map.prototype.getOrInsert() and getOrInsertComputed() (also on WeakMap) plus Iterator.concat()[[4]](#cite-4). Both are small, but getOrInsertComputed removes the common get-check-set pattern in caches. - Undici 8.0.2 in the 26.0.0 release, which backs fetch. If you pin a custom undici dispatcher or agent, retest it. Removed and deprecated in 26.0.0 These are the entries that break builds, taken from the Deprecations and Removals section of the changelog[[4]](#cite-4): - http.Server.prototype.writeHeader() is gone. Use writeHead(). - The legacy _stream_wrap, _stream_readable, _stream_writable, _stream_duplex, _stream_transform and _stream_passthrough modules are removed. Old packages that require('_stream_readable') will throw. Import from node:stream. - --experimental-transform-types is removed, which hits anyone running TypeScript files through that flag. - module.register() is now runtime-deprecated, so loader setups built on it print a warning. - DEP0182 moved to end-of-life, DEP0203 and DEP0204 became runtime deprecations in crypto, and DEP0201 in stream became a runtime deprecation. I did not look up what each DEP number covers in the deprecations page, so grep your warnings for those codes. - Platform floor: the build now needs GCC 13.2 and drops Python 3.9[[4]](#cite-4). That matters if you compile Node yourself or build native addons on old CI images. One more change that is easy to miss: localStorage now returns undefined when no storage file is configured, per the semver-major list in the same release[[4]](#cite-4). Code that assumed an object will fail at the first property access. Useful additions since 26.0.0 Node 26 already has twelve minor releases (26.1.0 to 26.12.0), so some features you may want are not in 24. These are the ones listed under Notable Changes, with the release that added them[[4]](#cite-4): | Version | Date | Addition | | --- | --- | --- | | 26.1.0 | 2026-05-07 | Experimental node:ffi module, behind --experimental-ffi | | 26.2.0 | 2026-05-20 | stream.compose marked stable; writeInformation for 1xx responses | | 26.8.0 | 2026-08-26 | TracingChannel in diagnostics_channel marked stable | | 26.9.0 | 2026-09-16 | node:ffi enabled by default | | 26.11.0 | 2026-10-07 | Buffer.stringLength(), isLatin1, Alpine Linux promoted to tier 2 support | | 26.12.0 | 2026-10-14 | http.websocketMask() and websocketUnmask() | The ffi row deserves a second look. The 26.1.0 notes say the API is inherently unsafe, since bad pointers or wrong signatures can crash the process or corrupt memory[[4]](#cite-4). Being on by default in 26.9.0 does not make it safe. Treat it as something you do not use in production code until it has a track record. Also from the notes: the 26.3.0 release flags a risk that macOS Universal Binaries may not be maintained for the full life of Node 26, though the stated intention is to keep shipping them as long as practical. Upgrade checklist: Node 24 or 22 to Node 26 1. Install 26 beside your current version Do not replace anything yet. Install 26 next to what you run, so you can switch per shell. # nvm: install, then carry global packages over from 24 nvm install --reinstall-packages-from=24 26 nvm use 26 # fnm fnm install 26 fnm use 26 # Volta (pins the version in package.json) volta install node@26 volta pin node@26 The --reinstall-packages-from flag is documented in the nvm README[[5]](#cite-5), and fnm install [VERSION] and fnm use [VERSION] are in the fnm command reference[[6]](#cite-6). The Volta commands are from memory of its docs and I did not re-verify them today, so run volta --help if one fails. After 28 October, nvm install --lts should resolve to 26, but check node -v rather than trusting it. 2. Declare what you support in package.json { "engines": { "node": ">=24 <27" } } Widening engines to cover both 24 and 26 lets you run both in CI during the transition without npm complaining. Add "engine-strict=true" to .npmrc if you want npm to enforce it. Also drop a .nvmrc or .node-version file: the actions/setup-node action reads either through its node-version-file input[[7]](#cite-7). If you use Corepack for package managers, run corepack --version after installing 26 to confirm it is present, because Corepack distribution has changed across recent Node releases and I did not trace the details. 3. Run a CI matrix on both lines jobs: test: runs-on: ubuntu-latest strategy: matrix: node: [24, 26] steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v7 with: node-version: ${{ matrix.node }} - run: npm ci - run: npm test The current major of actions/setup-node is v7; its latest release, v7.1.0, was published on 8 October 2026[[7]](#cite-7). The README also notes that automatic package-manager caching is on by default and can be turned off with package-manager-cache: false for workflows that handle sensitive data. For a full pipeline around this step, see our [GitHub Actions CI/CD pipeline for Node.js](/blogs/github-actions-ci-cd-nodejs-complete-pipeline-2026). 4. Update Docker base images Docker Hub already carries the 26 tags: node:26, node:26-slim, node:26-alpine, node:26-bookworm-slim and node:26-trixie-slim, plus pinned forms like node:26.12.0[[8]](#cite-8). Pin a major (node:26-slim) in production, and pin the full version if you need byte-identical rebuilds. I could not confirm from a primary source whether Docker will add an lts alias pointing at 26 on 28 October, so do not depend on one. If your image builds native addons on Alpine, remember that 26.11.0 promoted Alpine to tier 2 support[[4]](#cite-4), which is good news but not a reason to skip a test build. Our [production Docker Compose guide for Node.js](/blogs/docker-compose-production-nodejs-redis-postgres-2026) covers the rest of the stack. How to test before you switch 1. Run the full suite on 26. Start with unit tests, then integration tests against real databases and queues. Failures from removed APIs show up as thrown errors, so they are loud. 2. Surface deprecations. Run node --trace-deprecation your-entry.js or set NODE_OPTIONS=--trace-deprecation in CI. Runtime deprecations print a warning with a stack, and the stack usually points at a dependency, not your code. To make the suite fail on them, use --throw-deprecation. 3. Grep for the removed names. Search your source and node_modules for writeHeader(, _stream_, module.register and experimental-transform-types. Dependencies are the usual source of old stream imports. 4. Rebuild native addons. Delete node_modules and run npm ci on 26 so every addon compiles against the new V8. Anything built with node-gyp needs a toolchain that supports GCC 13.2 on Linux; addons that ship prebuilt binaries need a release that targets Node 26's ABI. If a package has no 26 build, that is your blocker, not Node. 5. Diff your fixtures and configs. When a test fails on a data shape, normalize the sample before comparing. Paste it into the [JSON formatter](/tools/json-formatter) to normalize them before comparing, or run YAML configs through the [YAML to JSON converter](/tools/yaml-to-json-converter). 6. Soak in staging. Run 26 under real traffic in staging for a week and watch memory and latency. A new V8 changes garbage collection behavior in ways unit tests do not show. If your project is a command-line tool, our [Node.js and TypeScript CLI guide](/blogs/build-cli-tool-nodejs-typescript-complete-guide-2026) has patterns for testing across several runtime versions. When to upgrade now, and when to wait | Your situation | Recommendation | Reason | | --- | --- | --- | | Production on Node 20 | Move now, to 24 at minimum | End-of-life since 30 April 2026; no security fixes | | Production on Node 22 | Plan the move before spring 2027; test on 26 now | Ends 30 April 2027 | | Production on Node 24 | Wait. Test on 26, switch in 2027 | Supported until 30 April 2028 | | New project | Start on 24 today, or 26 after 28 October | 26 is the longer runway (to April 2029) | | Library author | Add 26 to your CI matrix now | Users will adopt 26 as soon as it is LTS | The reason to wait on 24 is boring and sound: LTS lines get fixes that have been audited by the release team, and a Current line that has just changed status has had less production exposure than the fifth month of a stable one. Node 26 has had five months of Current releases, and the 26.3.1 and 26.5.1 releases each shipped a list of CVE fixes[[4]](#cite-4). Active patching counts in its favor, though it also shows the line has had real security work to do. If you do move before the LTS date, treat it as a normal Current-line adoption: pin an exact version, watch the changelog for each minor, and be ready to roll back. Frequently asked questions What date does Node 26 become LTS? The official schedule file lists 28 October 2026 as the LTS start for Node 26[[1]](#cite-1). The README says dates are subject to change, so confirm on the day. This post was written on 19 October, before that date. Is Node 24 still safe to use after 20 October? Yes. From 20 October 2026 it is in Maintenance, which means critical bug fixes and security updates, until 30 April 2028[[1]](#cite-1). You lose new-feature backports, not security fixes. When does Node 22 reach end-of-life? 30 April 2027, according to the schedule file[[1]](#cite-1). It has been in Maintenance since 21 October 2025. Is Node 20 end-of-life already? Yes. The schedule lists 30 April 2026 as its end date[[1]](#cite-1). If you still run it, upgrade to 24 or 26 rather than hunting for patched builds. Does Node 26 have a codename? Not in the schedule file as of 19 October 2026, where the codename field for v26 is empty[[1]](#cite-1). Earlier LTS lines got element names such as Jod and Krypton, but I will not guess one for 26. Can I skip Node 24 and go from 22 straight to 26? Yes, and many teams will. You inherit every removal and deprecation between the two lines, plus the npm 10 to 11 change, so read the changelogs for 24 and 26 and run the test steps above. I only reviewed the 26 changelog for this post. Sources 1. nodejs/Release: schedule.json (read 19 October 2026) 2. Node.js release feed, nodejs.org/dist/index.json (read 19 October 2026) 3. nodejs/Release README: release schedule and phases (read 19 October 2026) 4. Node.js 26 changelog, CHANGELOG_V26.md (read 19 October 2026) 5. nvm README: installing and --reinstall-packages-from 6. fnm command reference 7. actions/setup-node README and releases (v7.1.0, 8 October 2026) 8. Docker Hub: official node image tags (read 19 October 2026) --- Originally published at wowhow.cloud
파편화 멈춰 fnm#1433
Switched from nvm to fnm and now each new tab in Ghostty starts ~800ms faster. Neat! schniz/fnm
Memang _worthed_ beralih dari #nvm ke #fnm schniz/fnm #nodejs
I found a new node version manager that's in nixpkgs and works with nix schniz/fnm