Search nomadLab

Node vs Deno vs Bun: Pick the Support Window, Not the Benchmark

Node publishes end-of-life dates four years out. Deno maintains one LTS line at a time and has already left a two-month gap between two of them. Bun's security policy lists one supported version: 1.x.

Updated

Runtime comparisons still open with requests per second, and the number is still mostly irrelevant, because a service that talks to Postgres spends its time waiting on Postgres. There is a question underneath that ages better and takes ten minutes to answer: if you ship this runtime in a container today, how long will somebody publish a security patch for the exact version you shipped?

The three answers are not close to each other. All dates below came off the projects’ own release schedules and security policies on 23 August 2026.

A timeline from 2025 to 2029 comparing support windows. Node.js 22 is supported until April 2027, Node.js 24 until April 2028, and Node.js 26 until April 2029. Deno's LTS channel covers only the 2.9 line, from July 2026 to January 2027, a seven-month window. Bun publishes no end date at all; its security policy lists 1.x as the supported version, so the supported build is whatever shipped most recently. How far ahead each project has committed to patching Node 22 ends 30 Apr 2027 Node 24 LTS ends 30 Apr 2028 Node 26 ends 30 Apr 2029 Deno 2.9 LTS ends 31 Jan 2027 Bun 1.x whatever shipped last, no published end date 2025 2026 2027 2028 2029 Node 25 is not here. It started Oct 2025 and was already end of life on 1 June 2026.
Bars run from each line's release date to the end date in the project's own schedule. Node 22 is clipped at the left edge; it shipped in April 2024. Deno's bar is the LTS maintenance window, not the 2.9 release date.

Node publishes dates you can put in a ticket

Node’s release schedule lives in a JSON file in the repository, which means the dates are not a blog post someone has to remember to update. Version 22 ends 30 April 2027. Version 24 entered LTS on 28 October 2025 and ends 30 April 2028. Version 26 shipped 5 May 2026, becomes LTS on 28 October 2026, and ends 30 April 2029. Version 27 is already in the file with an end date of 30 April 2030.

Odd-numbered lines are the trap, and Node 25 is the current example. It started 15 October 2025 and reached end of life on 1 June 2026, about seven and a half months. A base image pinned to node:25 stopped getting security releases in the middle of this year, and nothing about the tag says so.

That predictability is the actual product. Three years of patches on a line you can name in advance is what lets a platform team schedule a runtime upgrade a year out instead of discovering it during an audit.

Deno maintains one LTS line, and it has skipped a month

Deno ships a stable minor every twelve weeks and keeps exactly one long-term support line alive at a time. You get onto it with deno upgrade lts, and backports are limited to security patches and critical bug fixes; API changes and features do not come back.

The history of that channel is short enough to print in full:

LTS lineMaintenance startMaintenance end
v2.11 Feb 202530 Apr 2025
v2.21 May 202531 Oct 2025
v2.51 Nov 202530 Apr 2026
v2.91 Jul 202631 Jan 2027

Two things fall out of that table. The windows are three to seven months, not three years, so an LTS pin in Deno means moving twice a year rather than never. And there is a gap: the 2.5 line stopped on 30 April 2026 and the 2.9 line did not start until 1 July, leaving two months where the LTS channel had no maintained line under it. If your compliance story is “we run the LTS build,” it is worth knowing that the LTS build can be between lines.

One more detail that bites in Dockerfiles. Deno’s channel is baked into the binary, and a bare version number always resolves to the stable build. Running deno upgrade 2.9.3 on an LTS machine silently moves it to the stable channel, because 2.9.3 exists as two byte-different builds with the same version string. To stay on LTS you upgrade with deno upgrade lts and nothing else.

Bun’s supported version is the newest one

Bun’s SECURITY.md has a supported-versions table with a single row: 1.x.x, checked. There is no end-of-life date anywhere, no LTS channel, and no line that keeps receiving fixes after the next minor lands. Bun 1.3.14 shipped on 13 May 2026; Bun 1.4.0 shipped on 20 August 2026. The patch for anything found today will be in whatever ships next.

For a CLI, a build step, or a test runner in CI, that is fine and arguably correct: you upgrade constantly anyway. For a long-lived service in a regulated shop, “upgrade to the latest minor” is the entire patch process, and it is worth deciding on purpose rather than finding out during an incident.

The other question: whose bug is it when a package breaks

Both challengers publish a per-module compatibility table, and reading the two tables side by side tells you more about their goals than any benchmark does.

Deno marks six node: modules unsupported: cluster, domain, repl, sea, trace_events, and wasi. Their exports are non-functional stubs. Sixteen more, including http, https, net, tls, dns, worker_threads, and zlib, are marked partial. Deno is a runtime with its own web-standard surface that also runs Node code, and the table is where it says which parts it decided not to carry.

Bun marks exactly one module as not implemented, node:sea, and twenty as partial, including child_process, cluster, crypto, tls, and worker_threads. The stated position is stronger than the table: “If a package works in Node.js but doesn’t work in Bun, we consider it a bug in Bun.” That is a promise about triage, not a claim of completeness, and it is the reason Bun’s compatibility has closed as fast as it has.

The practical difference is who does the work when something breaks. On Bun, a compatibility failure is a bug report against the runtime. On Deno, if the module is one of the six, it is not a bug, it is the documented answer, and the work is yours. Neither is worse. They point at different jobs.

What I would actually do with this

If the thing you are choosing a runtime for will still be running in two years, and somebody other than you will have to patch it, Node is the only one of the three that has published a date for the version you would ship, and Node 24 is that version until October, when 26 takes over.

If you want Deno’s permissions model or its TypeScript-without-a-build-step story, take it and put the LTS end date in the calendar with the upgrade already scheduled. Seven months is a real window; it is just not the window most teams assume when they read “LTS.”

If you want Bun, the trade is explicit now: you are choosing a runtime whose patch channel is the release channel. That is a good fit for tooling and CI, where you were going to run the newest version anyway, and a decision that needs a named owner anywhere else.

The check that settles all of this for your own stack is short. Read the version your Dockerfile actually pins, find that version in the project’s schedule or security policy, and see whether a date comes back. For one of these three, no date will.

Keep reading