Release Lines & LTS
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
This page is written against Node.js 26 (26.10.0, released 2026-09-21), which becomes LTS on 2026-10-28, with the differences of Node.js 24 "Krypton" (24.21.0, Active LTS) and Node.js 22 "Jod" (22.23.3, Maintenance) called out. The schedule was checked on 2026-10-03 against the official release schedule file and the Node.js previous-releases page, and every code example was run on Node.js 26.10.0, 24.21.0 and 22.23.3. Release dates move by a few days, so treat the tables as a dated snapshot and confirm them against the official sources listed in References before planning an upgrade. The APIs used here (process.version, process.versions, process.release) are stable.
Why release lines matter
Node.js is not one product with one version number: it is a set of release lines, each identified by its major version (22.x, 24.x, 26.x). A line receives bug fixes, security fixes and, while it is young, new features, and then it is retired. Choosing a line decides how long you get security patches, which V8 and libuv you run, which APIs exist and which native add-ons will load, because the module ABI number changes with every major version.
Every major version is built from the same main branch, so a feature that lands in 26.x usually reaches 24.x and sometimes 22.x as a backport. Backports are limited to what is safe for a line in its current phase, which is why the phase of a line matters as much as its number.
Release phases
A line moves through up to four phases. Which phases it visits depends on the model it was released under.
| Phase | What you get | What is allowed to land |
|---|---|---|
Current |
The newest line, six months from the first release. It receives the latest V8 and the newest features. |
New features and semver-minor changes, bug fixes and security fixes. Semver-major changes are made only on the next line. |
Active LTS |
Long-term support: the line recommended for general use, with a stable feature set. Starts in October for even-numbered lines. |
Security fixes, bug fixes and selected backports that are low risk. Features arrive only when they are considered safe. |
Maintenance |
The line is winding down. Starts about 12 months after LTS began. |
Critical bug fixes and security fixes only. |
End-of-Life (EOL) |
No releases and no security patches. The line may still be installed, but it is unsupported. |
Nothing. Commercial support is available through third parties for lines past Maintenance. |
Odd-numbered lines up to Node.js 25 never reached LTS: they were Current for six months, then moved to Maintenance and EOL a few months later. Even-numbered lines became LTS in October after their April release and are supported for about 30 months in total. LTS lines are given a codename taken from the chemical elements, in alphabetical order: Iron (20), Jod (22, the German name of iodine), Krypton (24). Node.js 26 has no codename in the release schedule file yet at the time of writing.
You can read the codename from inside a running process: process.release.lts holds the codename of an LTS line and is undefined on a line that is not (yet) LTS.
import process from 'node:process';
const { node, v8, uv, modules, napi } = process.versions;
console.log({
version: process.version,
lts: process.release.lts ?? 'not LTS',
node,
v8,
uv,
abi: modules,
napi,
});
Output on Node.js 26, 24 and 22:
{ version: 'v26.10.0', lts: 'not LTS', node: '26.10.0', v8: '14.6.202.34-node.34', uv: '1.52.1', abi: '147', napi: '10' }
{ version: 'v24.21.0', lts: 'Krypton', node: '24.21.0', v8: '13.6.233.17-node.53', uv: '1.52.1', abi: '137', napi: '10' }
{ version: 'v22.23.3', lts: 'Jod', node: '22.23.3', v8: '12.4.254.21-node.57', uv: '1.51.0', abi: '127', napi: '10' }
The ABI number (process.versions.modules) differs for each major line, which is why native add-ons compiled with node-gyp must be rebuilt after a major upgrade, while add-ons built on Node-API (napi) keep working across lines.
The schedule
The authoritative schedule is the schedule.json file of the nodejs/Release repository; the previous-releases page of nodejs.org renders it. The table below shows the lines that matter in October 2026.
| Line | Codename | Current from | Active LTS from | Maintenance from | End-of-Life | Status |
|---|---|---|---|---|---|---|
20 |
Iron |
2023-04-18 |
2023-10-24 |
2024-10-22 |
2026-04-30 |
EOL |
22 |
Jod |
2024-04-24 |
2024-10-29 |
2025-10-21 |
2027-04-30 |
Maintenance |
24 |
Krypton |
2025-05-06 |
2025-10-28 |
2026-10-20 |
2028-04-30 |
Active LTS |
25 |
none |
2025-10-15 |
never |
2026-04-01 |
2026-06-01 |
EOL |
26 |
none yet |
2026-05-05 |
2026-10-28 |
2027-10-20 |
2029-04-30 |
Current |
27 |
none yet |
2027-04-22 (alpha from 2026-10-28) |
2027-10 (new model) |
not applicable |
2030-04-30 |
Alpha not yet started |
Two dates are close: Node.js 24 moves to Maintenance on 2026-10-20 and Node.js 26 becomes Active LTS on 2026-10-28, so for about a week no line is both Active LTS and recommended for new projects. In practice Node.js 24 remains a safe production choice through the whole of its Active period and well into Maintenance.
Because the table is data, a script can compute the phase of each line from the schedule file instead of a person remembering it. The following example reads the file and classifies every line from Node.js 20 on.
const url = 'https://raw.githubusercontent.com/nodejs/Release/main/schedule.json';
const schedule = await (await fetch(url, { signal: AbortSignal.timeout(10_000) })).json();
const today = new Date().toISOString().slice(0, 10);
function phase(line) {
if (today >= line.end) return 'EOL';
if (line.maintenance && today >= line.maintenance) return 'Maintenance';
if (line.lts && today >= line.lts) return 'Active LTS';
if (today >= line.start) return 'Current';
return 'Upcoming';
}
const rows = Object.entries(schedule)
.filter(([name]) => Number.parseInt(name.slice(1), 10) >= 20)
.map(([name, line]) => ({
line: name,
codename: line.codename ?? '',
phase: phase(line),
lts: line.lts ?? '',
maintenance: line.maintenance ?? '',
end: line.end,
}));
console.table(rows);
Output on 2026-10-03 (Node.js 26; the global fetch and AbortSignal.timeout need no import):
┌─────────┬───────┬───────────┬───────────────┬──────────────┬──────────────┬──────────────┐
│ (index) │ line │ codename │ phase │ lts │ maintenance │ end │
├─────────┼───────┼───────────┼───────────────┼──────────────┼──────────────┼──────────────┤
│ 0 │ 'v20' │ 'Iron' │ 'EOL' │ '2023-10-24' │ '2024-10-22' │ '2026-04-30' │
│ 1 │ 'v21' │ '' │ 'EOL' │ '' │ '2024-04-01' │ '2024-06-01' │
│ 2 │ 'v22' │ 'Jod' │ 'Maintenance' │ '2024-10-29' │ '2025-10-21' │ '2027-04-30' │
│ 3 │ 'v23' │ '' │ 'EOL' │ '' │ '2025-04-01' │ '2025-06-01' │
│ 4 │ 'v24' │ 'Krypton' │ 'Active LTS' │ '2025-10-28' │ '2026-10-20' │ '2028-04-30' │
│ 5 │ 'v25' │ '' │ 'EOL' │ '' │ '2026-04-01' │ '2026-06-01' │
│ 6 │ 'v26' │ '' │ 'Current' │ '2026-10-28' │ '2027-10-20' │ '2029-04-30' │
│ 7 │ 'v27' │ '' │ 'Upcoming' │ '' │ '2027-10-20' │ '2030-04-30' │
└─────────┴───────┴───────────┴───────────────┴──────────────┴──────────────┴──────────────┘
Source: Node.js Releases (previous releases and release schedule), schedule.json in the nodejs/Release repository
The script is also a small illustration of the new model: the entry for v27 has an alpha date and no lts key, and its maintenance field holds 2027-10-20, the date the schedule file records for the October 2027 promotion. The announcement describes that moment as Node.js 27 entering LTS, so the helper above reports Upcoming for it until the schedule file is reshaped for the new model.
The annual model from Node.js 27
The Node.js release team announced that, starting with Node.js 27, the project moves from two major releases per year to one. Node.js 26 is the last line released under the old model; its schedule above does not change.
| Aspect | Up to Node.js 26 | From Node.js 27 |
|---|---|---|
Majors per year |
Two: even-numbered in April, odd-numbered in October |
One: April, with LTS promotion in October |
Which lines become LTS |
Only even-numbered lines |
Every line |
Early testing |
Nightly builds and the odd-numbered Current line |
An Alpha channel from October to March, where semver-major changes are allowed |
Alpha versions |
Not applicable |
Semver prerelease format, for example |
Phases |
Current (6 months), Active LTS (12), Maintenance (18) |
Alpha (6 months), Current (6), LTS (30), then End-of-Life |
Total support |
About 30 months for an LTS line |
36 months from the first Current release to End-of-Life |
Version numbers |
Increase by one per half-year |
Follow the calendar year of the first Current release: 27.0.0 in 2027, 28.0.0 in 2028 |
The Alpha channel is meant for library authors and CI pipelines, not for production. Alpha releases are signed, tagged and run through CITGM, the tool that runs the test suites of major open-source packages against the upcoming version. They are different from nightly builds, which are untested automated builds from main: an Alpha may leave out a change that is on main, for example when the change removes an API that is only being runtime-deprecated in that Alpha. The announcement asks library authors to add Alpha to their CI early, because a project that tests only on LTS lines can report bugs only after they affect its users.
The announcement lists the following sequence for the lines after Node.js 27, each with its Alpha in October of the previous year, Current in April, LTS in October and End-of-Life in April three years after Current began: 28.x (Alpha October 2027, Current April 2028, LTS October 2028, EOL April 2031) and so on. The same page states that this schedule is not final and may be amended.
For an application team that already runs only LTS lines little changes: the LTS window stays about 30 months and the overlap between LTS lines is preserved, so the practical effect is that the yearly upgrade becomes the only upgrade, and the version number you adopt no longer says whether the line is odd or even.
Choosing a line for production
The Node.js project states that production applications should use Active LTS or Maintenance LTS lines. Current is for library authors and for trying features early. In October 2026 that leads to the following guidance.
| Situation | Choice |
|---|---|
New service started before 2026-10-28 |
Node.js 24 "Krypton": Active LTS, supported until 2028-04-30. Plan the move to 26 or 27 inside the 24 window. |
New service started after 2026-10-28 |
Node.js 26, which is then Active LTS and supported until 2029-04-30. A project starting in 2027 should read the Node.js 27 schedule before choosing. |
Existing service on Node.js 22 |
Plan the upgrade now. 22 is in Maintenance and reaches End-of-Life on 2027-04-30, the earliest deadline of the supported lines. |
Existing service on Node.js 20, 21, 23 or 25 |
Unsupported. Upgrade before anything else: End-of-Life lines no longer receive security fixes. |
Library or framework |
Test on every supported line and on the newest Current or Alpha. Declare |
Containers |
Pin the line in the image tag (for example a |
Patch releases inside a line are what deliver security fixes, so running the latest patch of a supported line matters more than the line itself. Pinning to an exact patch version without a rebuild schedule defeats the point of an LTS line.
Pinning the line in a project
The line a project expects should be written down where tools can read it. A .nvmrc file lets version managers select it, and the engines field of package.json lets npm refuse to install on the wrong line.
{
"name": "engines-demo",
"version": "1.0.0",
"private": true,
"engines": { "node": ">=24" }
}
Running npm install on Node.js 22 only warns, because engines is advisory by default. With the --engine-strict flag (or engine-strict=true in .npmrc) the install fails:
nvm use 22
npm install --engine-strict
npm error code EBADENGINE
npm error engine Unsupported engine
npm error notsup Required: {"node":">=24"}
npm error notsup Actual: {"npm":"10.9.9","node":"v22.23.3"}
Without the flag npm prints npm warn EBADENGINE Unsupported engine with the same details and continues. On Node.js 26 the strict install succeeds.
A .nvmrc containing an alias follows the newest LTS instead of a fixed number:
echo "lts/*" > .nvmrc
nvm use
Now using node v24.21.0 (npm v11.19.0)
The alias resolves to Node.js 24 until a newer line is promoted to LTS (26 on 2026-10-28), which makes lts/* convenient for a laptop and risky for a build: CI should name an explicit major version so that the LTS promotion does not change the build by surprise. The installation mechanics, including other version managers and the official Docker images, are in Installing Node.js.
Source: Node.js Releases on nodejs.org
Upgrade strategy
An upgrade between lines is mostly a matter of discovering what breaks before production does. A sequence that works for services:
-
Read the changelog of each major you cross. Node.js publishes the notable changes and the semver-major commits of each release on its blog and in the
doc/changelogsfolder of the repository, and a deprecation that is documentation-only in one line becomes a runtime warning, and later an error, in later ones. -
Run the test suite on the new line in CI next to the current one, as a second matrix entry, before changing any runtime.
-
Turn deprecations into failures in that run, so they cannot hide in logs.
-
Rebuild native add-ons (the ABI number changed) and check which dependencies publish prebuilt binaries for the new line.
-
Roll out gradually, then retire the old line from CI.
Deprecation flags are the most useful tool for the third step. The example below calls url.parse(), which Node.js 24 and 26 deprecate for application code (DEP0169, it is documentation-only on 22).
import { parse } from 'node:url';
import { styleText } from 'node:util';
console.log(parse('https://example.com/a?b=1').pathname);
console.log(styleText('green', 'done'));
Run on Node.js 26 and 24 the script prints the result, then a warning; on Node.js 22 it prints no warning:
/a
done
(node:49732) [DEP0169] DeprecationWarning: `url.parse()` behavior is not standardized and prone to errors that have security implications. Use the WHATWG URL API instead. CVEs are not issued for `url.parse()` vulnerabilities.
Three flags change how deprecations behave:
| Flag | Effect |
|---|---|
|
Prints the stack trace of each deprecation warning, so you can find the calling line in your code or a dependency. Verified on Node.js 24. |
|
Throws instead of warning, so a test run fails. Verified on Node.js 26 and 24, where the script above throws after printing its output; Node.js 22 emits no runtime warning for this API and the script passes. |
|
Also emits warnings for deprecations that are documentation-only today and will become runtime warnings later. Verified on Node.js 22, where it makes the |
The deprecation numbers and the dates they changed are collected in Upgrading & What’s Changed, which also lists the behaviour that differs between 22, 24 and 26.
What changed recently
| Date or version | Change |
|---|---|
2026-04-30 |
Node.js 20 "Iron" reached End-of-Life. |
2026-05-05 |
Node.js 26.0.0 released: the last line under the two-a-year model. |
2026-06-01 |
Node.js 25 reached End-of-Life, about seven and a half months after its 2025-10-15 release: it was Current until 2026-04-01 and then spent two months in Maintenance, the usual pattern for odd-numbered lines. |
2026-10-20 |
Node.js 24 moves to Maintenance. |
2026-10-28 |
Node.js 26 becomes Active LTS, and the Node.js 27 Alpha channel opens with it. |
2027-04-22 |
Node.js 27.0.0 is scheduled as the first line under the annual model. |
2027-04-30 |
Node.js 22 reaches End-of-Life. |
Release history in numbers for the lines above: 22.23.3 (2026-09-23), 24.21.0 (2026-09-07) and 26.10.0 (2026-09-21) were the latest patches on 2026-10-03, so each supported line ships a patch roughly every few weeks.
Where the books are outdated
Both reference books were published in 2025 and describe the release model that ends with Node.js 26: Samer Buna, Efficient Node.js (O’Reilly, 2025) covers release lines in its first chapter, and Jonathan Wexler, Node.js Projects (O’Reilly, 2025) starts its projects from an installation of Node.js of that time. Two statements need updating when you apply them today. First, any advice built on "even-numbered lines are the production lines and odd-numbered ones are short-lived" stops holding with Node.js 27: every line becomes LTS, and the version number no longer carries that information. Second, any line named as current or recommended in a 2025 text has since moved on: Node.js 22 is now in Maintenance and Node.js 24 is the Active LTS that becomes Maintenance on 2026-10-20. The version checks in this page’s examples replace hard-coded lines with values read from the running process and the schedule file.