Bun vs Node.js
Bun's own test count says node:http passes 403 of 415 tests, and Node's own calendar says v20 died on 24 March 2026. Neither runtime charges a cent, so the bill arrives somewhere else.
Pick Node if your service needs an API Bun documents as missing — getPeerCertificate(), SNICallback, createHook with real async ids — or if a native addon in your tree has no build for NODE_MODULE_VERSION 147. Pick Bun if the addons and APIs you depend on survive its 403-of-415 node:http count, because the runtime itself will never appear on an invoice.
- Both runtimes cost nothing, so the invoice arrives as labour
- Node bills you on a date you did not choose
- Who should not pick Node for unbuilt TypeScript
- Bun's own node:http count leaves 415 − 403 = 12 tests failing
- Who should not pick Bun: teams requiring missing APIs
- Bun stays MIT after Anthropic's acquisition; check before migrating
- Cost arithmetic
- Decision matrix
- Migration path
- FAQ
At a glance
| Dimension | Bun All-in-one | Node.js Standards runtime |
|---|---|---|
| Product foundation | JavaScriptCore runtime, rewritten in Rust for 1.4 | Node core plus the npm ecosystem |
| Pricing model | Free · MIT · no paid tier | Free · MIT · support past Maintenance LTS via OpenJS partners |
| Hosting and deployment | Yes — a single executable | Yes — your own machines |
| Runtime and integration | Bun.serve HTTP and WebSocket server built in | node:http servers, cluster workers, inspector for tracing |
| AI-related capabilities | Claude Code ships as a Bun executable (Bun's statement) | Type stripping on by default; no type checking |
| Lock-in risk | Low: MIT, npm-compatible, runs Node projects | None: MIT, runs on every platform |
| Vendor | Bun → | Node.js → |
1. Both runtimes cost nothing, so the invoice arrives as labour
Bun is MIT. Node.js is MIT. Neither sells a paid tier, so the bill is engineer-hours on three meters: the release calendar, the compatibility surface and the native-module ABI.
For teams maintaining a Node service: as of 10 November 2026, both runtimes have no price.
- Node: Node publishes a release calendar; v26 Lithium, v24 Krypton and v22 Jod are LTS.
- Bun: 1.4.0 (20 August 2026) combined the Rust rewrite with the compatibility-target move from 24.3.0 to 26.3.0 and native ABI 147.
For Bun 1.4, check whether your native addons have builds for NODE_MODULE_VERSION 147.
2. Node bills you on a date you did not choose
Node's release page reads like a work schedule:
| Major | Status | Key date |
|---|---|---|
| v27 | Alpha | first released 29 Oct 2026 |
| v26 Lithium | LTS | first released 5 May 2026 |
| v25 | EOL | 31 Mar 2026 |
| v24 Krypton | LTS | first released 6 May 2025 |
| v22 Jod | LTS | first released 24 Apr 2024 |
| v20 Iron | EOL | 24 Mar 2026 |
Support window and release cycle:
- LTS "typically guarantees that critical bugs will be fixed for a total of 30 months" (Node's wording).
- Starting with v27, the cycle becomes annual: every major gets six months of Alpha, six of Current, then LTS.
Read it as a penalty table. It punishes the team that adopts a major and forgets it: v20 lived from 17 Apr 2023 to 24 Mar 2026, about 35 months.
If production still runs Node 20, plan a move to Node 24 or 26 LTS: Node 20 reached EOL on 24 March 2026.
3. Who should not pick Node for unbuilt TypeScript
Type stripping is on by default, stable since v25.2.0 / v24.12.0. Node executes erasable syntax without type checking; other syntax needs separate tooling.
// runs on Node 26 with no flags: every annotation here is erasable
type Host = string;
const host: Host = process.env.HOST ?? "localhost";
console.log(`binding ${host}`);
enumthrowsERR_UNSUPPORTED_TYPESCRIPT_SYNTAX.
Node 26 also refuses:
namespacewith runtime code, parameter properties, import aliases: same error.tsxfilespaths, becausetsconfig.jsonis ignored- TypeScript inside
node_modules - parsing without WebAssembly:
ERR_WEBASSEMBLY_NOT_SUPPORTED
This meter punishes codebases built on enums and path aliases; for them, the build step stays.
4. Bun's own node:http count leaves 415 − 403 = 12 tests failing
Bun says it plainly: "Bun is not 100% compatible with Node.js yet." Bun's own 1.4 counts against the Node 26.3.0 suite:
| Module | Bun 1.4 passes | Not passing |
|---|---|---|
| node:http | 403 / 415 | 415 − 403 = 12 |
| node:fs | 349 / 358 | 358 − 349 = 9 |
| node:module | 68 / 93 | 93 − 68 = 25 |
| node:test_runner | 28 / 81 | 81 − 28 = 53 |
| node:inspector | 22 / 110 | 110 − 22 = 88 |
| node:sqlite | 18 / 18 | 18 − 18 = 0 |
On node:http that is 12 ÷ 415 ≈ 2.9%, the failing 3%. Bun says 1.4 made 1,517 more Node tests pass.
The ABI tax: 1.4 moved the compatibility target from 24.3.0 to 26.3.0.
NODE_MODULE_VERSIONis now 147, so native addons built for Node 24 must be rebuilt.
Check required inspector APIs; module pass counts are not application coverage.
5. Who should not pick Bun: teams requiring missing APIs
Bun's compatibility docs name the gaps. Map each to a service you run:
- HTTP servers tuned for a load balancer.
http.Serverdoes not extendnet.Server, thekeepAlive/keepAliveInitialDelayserver options are no-ops, andlisten(handle)is ignored. Your keep-alive tuning silently does nothing. - mTLS and multi-tenant TLS. node:https lacks
getPeerCertificate()andgetCipher()and does not supportSNICallback. Retain Node if you require these APIs. - Signing code. BoringSSL-backed node:crypto lacks
ed448,x448,rsa-pssanddsakeys, has nosecp256k1curve and nochacha20-poly1305. - APM tracing.
async_hookscreateHookis a stub and async ids are always 0; correlation by async id collapses. clusterfleets on macOS or Windows. HTTP load-balancing across workers only works on Linux, viaSO_REUSEPORT.
AsyncLocalStoragecontext does not propagate into Worker/BroadcastChannel events in Bun.
6. Bun stays MIT after Anthropic's acquisition; check before migrating
Anthropic acquired Bun on 2 December 2025. Claude Code ships as a Bun executable (Bun's statement), and Bun's founder wrote then: "Today, Bun makes $0 in revenue." Bun stays MIT.
The acquisition changes who pays for Bun's roadmap. It does not change the licence, and it does not change the API surface you have to test.
Check today:
- List every
.nodeaddon in the tree and whether it has a build forNODE_MODULE_VERSION147. - Check required HTTP APIs,
createHook, distinct async IDs,getPeerCertificateandSNICallbackagainst Bun's gaps. - Write down the EOL date of the Node major running in production.
Cost arithmetic at volume
These are computed from the published unit prices, not measured. Each row prints its own formula, so you can replace our assumption with your traffic and redo it.
Decision matrix
Rows are the situation you are actually in. Pick the row before you read the pricing page again.
| If this is you | Take | Because |
|---|---|---|
| HTTP service considering Bun after checking required server APIs | Bun only if required APIs are supported and the service passes its checks; otherwise retain Node | Bun's own node:http count is 403 / 415; check unsupported server options, http.Server inheritance and listen(handle), not just the pass count. |
| Repo with native addons built for Node 24 | Node, until every addon has a build for NODE_MODULE_VERSION 147 | Bun 1.4 targets Node 26.3.0, so addons built for 24 do not load until rebuilt. |
| Production tracing agent requiring createHook or distinct async IDs | Node | Keep Node if your tracing agent requires createHook or distinct async IDs: Bun documents the hook as a stub and async IDs as always 0. |
| TLS gateway requiring getPeerCertificate() or SNICallback | Node | Bun's node:https lacks getPeerCertificate() and does not support SNICallback. |
| Signing service that needs secp256k1 or ed448 keys | Node | Bun's BoringSSL-backed node:crypto has no secp256k1 curve and no ed448 key type. |
| Still on Node 20 in production | Node 24 or 26 LTS, not a runtime switch | v20 reached EOL on 24 March 2026, and an LTS move fixes that without adding Bun's compatibility gaps to the same sprint. |
| Healthy service on Node 22 Jod with no pain points | Neither — stay put | v22 is still LTS and was last updated 20 October 2026, so no date forces a move yet. |
| Internal scripts and CLIs that currently pull in a separate test runner, linter and formatter | Bun | Bun 1.4 ships bun test --parallel, bun lint, bun fmt and bun check in the same binary. |
Code and configuration
// One binary replaces four dev dependencies:
// bun install, bun test, bun lint and bun fmt are the same executable.
import { Database } from "bun:sqlite"; // built in, no addon to compile
const db = new Database(":memory:");
db.run("create table t (id integer primary key, name text)");
// Here it breaks: a native addon built for Node 24 will not load,
// because Bun 1.4 reports NODE_MODULE_VERSION 147.
// Node 26 strips types; it does not check them or transform them.
type Host = string;
const host: Host = process.env.HOST ?? "localhost";
console.log(`binding ${host}`);
// Here it breaks: an enum is not erasable, so Node throws
// ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX before your code runs.
enum Mode { Dev, Prod }
What you gain, what you give up
Bun
- JavaScript, TypeScript, JSX and bundling from one executable, so no separate tsc, test runner or bundler
- 1,517 more Node test-suite tests pass in 1.4, and node:sqlite passes 18 of 18 by Bun's own count
- Bun's own hello.js benchmark: 5.1 ms vs 27.2 ms start-up on Linux, 14.6 MB vs 44.5 MB peak memory
- MIT licensed after the Anthropic acquisition, with the Node compatibility target moved to 26.3.0
- node:http passes 403 of 415 of Node's own tests, and http.Server does not extend net.Server
- node:https lacks getPeerCertificate() and getCipher(), and SNICallback is unsupported
- NODE_MODULE_VERSION 147 means addons built for Node 24 must be rebuilt
- Bun's own memory table shows the Vite dev server at 233 MB vs 214 MB on Node
Node.js
- A dated calendar: v26 Lithium and v24 Krypton are LTS, and LTS typically guarantees fixes for 30 months
- Native addons are the default path, and ABI builds target the runtime you actually run
- TypeScript type stripping runs with no build step for erasable syntax
- Nothing to license, and commercial support exists through OpenJS partners after LTS ends
- Type stripping refuses enum, .tsx, parameter properties, import aliases and tsconfig paths
- TypeScript inside node_modules is refused outright
- Parsing requires WebAssembly, or the parser throws ERR_WEBASSEMBLY_NOT_SUPPORTED
- v20 and v25 both reached end of life in 2026, so the calendar forces migrations
Migration path
Moving to Bun starts with bun install reading the same package.json, but every native addon needs a build for NODE_MODULE_VERSION 147 before it loads, so the hook stampede is the first thing to schedule. Moving back is not automatic either: Bun's compatibility docs list the API gaps you will meet — getPeerCertificate(), SNICallback, async_hooks — and there is no tool that rewrites handlers around them. Run the service test suite under both runtimes before either direction, then keep the loser's addon builds around for a release or two.
Common questions
--experimental-transform-types was removed in v26.0.0.hello.js benchmark puts Linux start-up at 5.1 ms for Bun 1.4 versus 27.2 ms for Node.js 26; these are vendor measurements, not independent ones. Separately, Bun's peak-memory figures under load, with 1,000,000 requests and 64 connections, put the Vite dev server at 233 MB on Bun versus 214 MB on Node, so Bun is not lighter everywhere.Sources & verification
Prices and limits were read from these pages on 2026-11-10. Vendor marketing claims are quoted as claims. Anything derived is arithmetic on the numbers below.
https://bun.com/blog/bun-v1.4
https://bun.com/blog/bun-joins-anthropic
https://bun.com/docs/runtime/nodejs-compat
https://nodejs.org/en/about/previous-releases
https://nodejs.org/api/typescript.html
Keep reading
Docker vs Podman
Another open-source runtime where the licence sits on the developer tool, not the engine.
Prisma vs Drizzle
Two free libraries whose maintenance now depends on a vendor with a bill attached.