Adamarant
Start
Back to Field notes

Bun vs Node in 2026: where each runtime wins in production

Web Design and EngineeringSep 3, 20267 min read

Bun 1.3 ships a full-stack runtime, Node 24 ships stable TypeScript and a test runner. We run Bun for installs and tests, Node for what serves traffic.

Train track switches with yellow levers in daylight

Bun is a JavaScript runtime built on JavaScriptCore that ships a package manager, a test runner and a bundler inside the same binary, and aims to run existing Node code unchanged. Node is the runtime the npm registry was written against, and the default on almost every managed platform that will run your code for you.

The choice comes up at two moments: when you stand up a new service, and when install times or the CI bill start to hurt. In the State of JavaScript 2025 survey, 90% of respondents reported using Node and 21% reported using Bun, up four points year on year, with Deno at 11% (State of JS 2025). Those numbers are not a scoreboard. Most teams running Bun are also running Node, in different parts of the same repo.

The 30-second answer

Use Bun where the work is short-lived and you own the machine: installs, tests, scripts, local dev servers, one-off tooling. Use Node where the process is long-lived, faces the public, and pulls in packages you did not write. If you have to pick one runtime for a SaaS backend that must still be online in eighteen months, pick Node and let Bun earn its way in from the CI job.

What differs between Bun and Node

  • Engine. Bun runs on JavaScriptCore, the engine behind Safari. Node runs on V8. Different garbage collection behaviour, different JIT warm-up, different memory profile under sustained load.
  • Scope. Bun is runtime plus package manager plus test runner plus bundler. Node is a runtime that has since picked up a test runner and, in Node 24, a TypeScript type stripper.
  • Direction of compatibility. Bun implements Node's APIs. Node does not implement Bun's. Anything written against Bun.serve or bun:sqlite is Bun-only from that line forward.
  • Release cadence. Bun ships point releases most weeks. Node ships a major in April and October, and supports each LTS line for 30 months.
  • Where it runs managed. Node has a managed runtime on every serverless platform worth naming. Bun has one on Vercel Functions, in public beta, and none on AWS Lambda.

Where Bun wins

Installs and CI time

This is the least arguable win. Independent 2026 benchmarks put a cold install of a 50-dependency project under a second for bun install against roughly 14 seconds for npm, with pnpm in between and closing most of the gap once the CI cache is warm (PkgPulse benchmark). On a monorepo the saving is minutes per run, on every run, for every engineer.

You can take this win without adopting Bun as a runtime at all. bun install writes a lockfile and a conventional node_modules tree, and Node runs the result. If your install step is slow because the tree is large rather than because npm is slow, the fix sits upstream in dependency sprawl, and a faster installer only hides it.

One binary instead of four tools

A new Bun service needs no Jest, no Vitest, no tsx, no esbuild config. bun test runs TypeScript directly, bun build bundles, bun --hot reloads. Bun 1.3, released in October 2025, added built-in Postgres, MySQL and Redis clients, plus a dev server that will serve an index.html, resolve ES modules and hot-reload with no bundler config at all (Bun 1.3 release notes).

The saving shows up as config files you never write and never upgrade. On a small internal service that is worth more than any throughput number.

Scripts and local tooling

Anything you would have written as a shell script with a Node shebang belongs in Bun. It starts faster, reads TypeScript with no build step, and ships file and shell helpers in the standard library. Cold start is where the JavaScriptCore advantage is widest, and a script is all cold start.

Raw throughput, with an asterisk

Bun's synthetic HTTP benchmarks beat Node's by a wide margin. Bun 1.3 reported Express running 9% faster and Fastify 5.4% faster than the prior release, with JavaScript memory use down 10 to 30% (Bun 1.3 release notes). On a service that talks to Postgres on every request, the runtime is rarely the bottleneck, and the end-to-end difference collapses to low single digits. Treat throughput as a tiebreaker rather than a reason.

Where Node wins

Native addons

The usual blocker is native code: packages that compile against Node's N-API. sharp, bcrypt, better-sqlite3, most of what still goes through node-gyp, anything that ships a prebuilt binary per platform. Bun publishes its Node API coverage module by module and marks the partial implementations, including the N-API caveats (Bun Node.js API compatibility). Read that page against your own dependency tree before you commit, not after the first deploy fails.

Managed serverless

AWS Lambda's managed Node runtimes cover Node 22, 24 and 26, with Node 26 arriving as a public preview runtime in August 2026 (AWS Compute Blog). There is no managed Bun runtime on Lambda. Vercel does run Bun on Functions in public beta, currently for Next.js, Express, Hono and Nitro (Vercel Bun runtime docs), which is real movement, and public beta is still public beta for the process answering your customers. The same reasoning applies one level down when you pick edge runtime against Node runtime: the constraint is what the platform will operate for you, not what benchmarks well on a laptop.

The gap Node closed

Most of the 2023 case for Bun was ergonomics, and Node spent two years dismantling it. Node 24 runs node app.ts with stable type stripping: no tsc step, no build folder, no source maps to ship (Node.js TypeScript docs). require() of an ES module works by default since Node 23 (Node 23 release notes). node --test gives you assertions, mocks, coverage, watch mode and parallel execution with nothing installed (node:test docs). Node 24 is Active LTS with support to April 2028, and Node 26 enters LTS in October 2026 (Node.js releases).

Type stripping does not typecheck. It erases annotations and runs the result, so you still need tsc --noEmit in CI, which is exactly where the Go-based TypeScript compiler changes the arithmetic.

How to decide in ten minutes

  1. Grep your dependency tree for packages shipping a .node file or a node-gyp build. If the list is not empty, Node serves production.
  2. Check where the code deploys. Lambda, a base image you do not control, or a customer's own cluster all mean Node.
  3. Time the CI install step. Over 60 seconds and you should move that step to Bun this week, runtime untouched.
  4. Time the test suite. A Jest or Vitest run over 90 seconds justifies a bun test spike on a branch.
  5. Ask who carries the pager. Two years of production mileage in your team beats two weeks of it, whatever the benchmark says.

What we use and why

We run Node for anything that serves traffic and Bun for everything around it. Node executes our Next.js builds and our Supabase-backed API routes, because that is what the platform runtimes and our container images provide, and because those dependency trees contain native addons. Bun runs installs and unit tests in CI, and it runs the maintenance and content scripts in our repos, where a 40ms start beats a 300ms one several hundred times a day.

One rule holds the whole arrangement together: no Bun-only API in application code. No Bun.serve, no bun:sqlite, no Bun.file in anything that could end up on a server. Written that way, the runtime under a service stays a deployment decision we can reverse in an afternoon instead of a rewrite we have to schedule. The constraint costs us close to nothing today, and it is the reason our answer to this question can change next year without anybody getting hurt.

Sources

Photo by pavel ondera on Unsplash

Frequently asked questions

Is Bun stable enough to run production traffic in 2026?+

For a service you fully control, with a dependency tree you have checked against Bun's Node API compatibility page, yes. The risk is not the runtime crashing. The risk is a transitive dependency that compiles against N-API, a library that branches on process.versions.node, or a hosting platform with no managed Bun runtime, and all three surface late. Teams that run Bun in production usually scoped it to one service first and kept Node on everything else for a couple of quarters.

What does it cost to migrate back from Bun to Node?+

Close to nothing if you never imported a bun: module and never called a Bun global. In that case you change the Dockerfile base image and the CI command, and the code is untouched. It gets expensive the moment Bun.serve is your HTTP server or bun:sqlite is your database driver, because each of those is a rewrite of the layer that touches every request. Decide that boundary on day one, not during the incident.

If Node 24 runs TypeScript natively, do we still need tsc?+

Yes. Node's type stripping erases annotations and executes what is left, so a type error runs happily until it becomes a runtime error. Keep tsc --noEmit as a CI gate and use type stripping to delete the build step from local development and from scripts. The same applies to bun run on a .ts file: fast execution, zero type checking.

Where does Deno fit in this comparison?+

Deno sits at 11% usage in the State of JavaScript 2025 survey, roughly half of Bun's share, and it solves a different problem: permissions and a standard library designed from scratch, rather than Node compatibility at any cost. If your reason for looking past Node is a sandboxed execution model, Deno is the honest candidate. If your reason is install speed and tooling in one binary, Bun is the shorter path, because it asks less of your existing code.

Studio

Start a project.

We write about what we build. Tell us what you want to build.