Bun 2.0 Won. Node Isn't Dead.
Bun 2.0 went stable on January 7th. I switched my default package.json runner to bun the same week. Three months later, every new project I start is Bun. About half my older production systems are still Node, and I'm in no rush to change that.
The short version: the runtime war is over, and the answer is both.
The Numbers That Matter
I ran the same Hono app on a $12 Hetzner box on Bun 2.0 and Node 24 for two weeks. The results aren't subtle:
- HTTP throughput: Bun pushed roughly 185,000 req/s to Node's ~78,000 on the same hardware
- Cold start: ~12ms on Bun vs. ~120ms on Node for the same container
bun installvsnpm install: a clean install of a 230-dep Next.js project went from 38 seconds to 1.4 seconds- Test runner:
bun teston a 600-test suite ran in 4.2s vs. 31s on Vitest
These match what every benchmark on the internet is saying. For the first time, they also match what I see in production.
What Changed In 2.0
Bun 1.x was fast and unstable. Loading an obscure transitive dep would crash the process. Some Node APIs were missing. Some were broken in subtle ways. Every team running Bun in production had a private list of "things to never do."
Bun 2.0 closed almost all of that:
- 99.7% Node API compatibility — the holes are weird stuff most apps don't touch (
vm.Scriptcorner cases, someclustersemantics) - Built-in S3 client and Postgres driver —
import { sql } from "bun"and you have a typed Postgres connection with zero deps - Native Workspaces with hoisting that actually matches
pnpm— the deal-breaker for monorepos is fixed - Stable hot reload —
bun --hotworks for HTTP servers, websockets, and workers without the random restart loops 1.x had - TypeScript without a build step — direct execution of
.tsfiles, full decorator support, type stripping in production builds
Every framework I tried — Next.js 16, Hono, Fastify, Nest, Remix, the React Router v7 standalone — works without flags.
Where Node Still Wins
This is the part the breathless "Node is dead" takes get wrong.
Node has fifteen years of operational maturity. That isn't just lines of code. It's:
- The crash diagnostics. Node's heap dumps,
--inspect,clinic.js,0x, async-hooks tooling — Bun doesn't have any of this at the same quality yet. When a memory leak shows up in week two of a paying customer's deployment, I want Node. - The boring deployment story. Every PaaS, every cloud, every linux distro, every CI runner has Node working out of the box. Bun is almost there. Almost still means weird edge cases on certain ARM container images.
- The ecosystem of operational tools. OpenTelemetry, Sentry's profiler, Datadog APM, New Relic — the deepest integrations are still Node-first. Bun's compatibility layer mostly works, with occasional gaps.
- The institutional weight. Half my contract gigs use Node. None of them are switching this year. If you ship libraries, you ship for Node.
The pattern: Node is where I want my production money. Bun is where I want my development velocity.
How I Actually Split It
After a quarter of running both:
Bun for:
- Every new side project (revenue or not)
- Local dev across the board —
bun devis just better - CI runners that don't touch prod artifacts
- Build tooling —
bun buildreplacedtsup,vite build, andesbuildin three projects - Scripts. Every one-off
node script.tsis nowbun script.ts. No moretsxorts-nodeindevDependencies.
Node for:
- Existing production systems with real users
- Anything that touches a native binding I care about (sharp, canvas, native crypto modules with quirks)
- Long-lived workers where I trust the GC's behavior across weeks
- Customer-facing servers where APM coverage matters
The handoff is painless because they share a package.json. The same project usually has "dev": "bun dev" and "start": "node dist/server.js". Most of my CI matrices test both.
The Built-In Stuff Is The Real Story
The benchmark numbers get the headlines. The real shift is that Bun is becoming an opinionated platform, not just a runtime.
bun create. bun test. bun build. bun install. bun --hot. bun:sqlite. bun:s3. bun:sql. bun:redis (added in 2.0.3).
Open a new project, type one command, get a working dev loop, a typed Postgres connection, a test runner, a bundler, and a deployment artifact. No tsconfig.json arguments. No five build tools fighting each other.
For someone shipping solo, this is a bigger productivity win than the speed numbers. The fastest runtime in the world doesn't matter if you spent the day debugging tsx and esbuild plugins. Bun ate the toolchain.
What This Means
Three predictions, holdable:
- By end of 2026, more than half of new Node-shaped projects start on Bun. It's already happening in my GitHub trending feed.
- The "runtime war" framing dies. Bun, Deno, and Node converge into a single mental model: "JavaScript runtime, choose flavor." The differences become preference, not religion.
- Native modules get harder, not easier. Bun supports N-API, but the ecosystem of native bindings is still Node-shaped. This is the friction point that doesn't disappear soon.
If you're starting today, start on Bun. If you're running on Node and it's working, leave it alone. The bridge between them is good enough that switching isn't the question. When you switch is.
Bun won the speed argument. Node still owns the trust argument. They're not the same argument.
Sources: