IBRAVRA Media Network
Software Reviews • 12 min read

Bun vs Deno vs Node: JavaScript Runtime Performance Playbook

Bun 1.4, Deno 2.9 and Node 26 compared on startup, HTTP throughput, TypeScript and npm compatibility: vendor benchmarks read honestly, with reproducible server-count cost math at stated volumes.

By Enow A. Jovial • Published 2026-11-14

Bun vs Deno vs Node: JavaScript Runtime Performance Playbook

Node.js is the runtime almost every server, library and hosting platform targets. Bun is a single binary bundling runtime, package manager, bundler and test runner, built on JavaScriptCore and now written in Rust. Deno is a V8 runtime with TypeScript built in, secure by default, and — since version 2 — able to run existing Node.js projects directly. The current lines, verified from the vendors' own pages on 14 November 2026: Node 26.13.1 LTS, Bun 1.4.4, Deno 2.9.8. This playbook compares the three on startup, HTTP throughput, TypeScript, npm compatibility, and the server count a given volume needs. Bundlers are a different layer — see the Vite vs Webpack vs esbuild bundler playbook.

Bun starts fastest and uses the least memory; Deno leads the hello-world throughput test on Deno's own page; Node is the reference every library targets and the only one with a formal LTS programme.

Three runtimes, three designs

Node.js runs JavaScript on V8, the engine inside Chrome, with libuv underneath for I/O. It is the reference implementation: every npm package is written against it first, and its release discipline is the most conservative — even-numbered releases move to Active LTS after six months in Current, and Node advises production use only of Active LTS or Maintenance LTS releases.

Deno is built in Rust on the same V8 engine, with TypeScript as a first-class citizen and a permission sandbox around every process. Code runs with no file, network or environment access until the operator grants it — the opposite default from Node, where a dependency can read anything the process can. Deno 2 removed the wall that kept it a niche choice: drop Deno into a repository with a package.json and node_modules and it runs, mixing npm: imports with native ES modules. JSR is its TypeScript-first registry, but npm is no longer a foreign country.

Bun is the outlier on engine choice: JavaScriptCore, the engine behind Safari, instead of V8. Bun 1.4, shipped 20 August 2026, rewrote the runtime from Zig to Rust, added 1,517 tests from Node's own test suite, and fixed over 2,900 issues. The pitch is consolidation — runtime, package manager, bundler, test runner, linter and formatter in one binary — and the price is youth.

The comparison table

DimensionNode 26 LTSDeno 2.9Bun 1.4Authority
TypeScriptNative, type stripping, v22.18.0+Native, first-classNative, transpile on runNode.js; Deno; Bun
npm compatibilityReference implementationRuns package.json directly97% of Node's tests on core modulesDeno; Bun
Security defaultOpen, full process accessSandboxed, permissions requiredOpen, full process accessDeno docs
Hello-world throughput56,300 req/s85,600 req/s81,900 req/sdeno.com chart
TypeScript startup50.0 ms19.6 ms14.7 msdeno.com chart

Bun and Deno win the speed columns, Node wins support, and Deno's security default is the only structural difference.

Startup time: where Bun's lead is real

Startup is the one measure where the gap is large, consistent across vendors, and easy to explain. Bun's docs state that Bun processes start 4x faster than Node.js, and its 1.4 blog gives the table: on Linux, Bun 1.4 starts in 5.1 ms against 27.2 ms for Node.js 26; on Windows, 15.5 ms against 40.1 ms. Deno's homepage chart measures TypeScript startup separately, on Apple M5: Bun 14.7 ms, Deno 19.6 ms, Node 50.0 ms — Bun 3.40x faster than Node, Deno 2.55x faster.

Linux startup advantage=27.25.1=5.33×,Windows startup advantage=40.115.5=2.59×TypeScript startup, Node vs Bun=50.014.7=3.40×,Node vs Deno=50.019.6=2.55×Daily restart saving=40×(27.2−5.1)=884 ms per day,884×22=19,448 ms≈19.4 seconds per month\text{Linux startup advantage} = \frac{27.2}{5.1} = 5.33\times, \qquad \text{Windows startup advantage} = \frac{40.1}{15.5} = 2.59\times \text{TypeScript startup, Node vs Bun} = \frac{50.0}{14.7} = 3.40\times, \qquad \text{Node vs Deno} = \frac{50.0}{19.6} = 2.55\times \text{Daily restart saving} = 40 \times (27.2 - 5.1) = 884 \text{ ms per day}, \qquad 884 \times 22 = 19{,}448 \text{ ms} \approx 19.4 \text{ seconds per month}

Read the last line honestly: a developer restarting a long-lived server 40 times a day saves under a second a day. Startup time is not a reason to move a monolith — it is a reason to move a CLI tool or a serverless function, where cold start is the user-visible latency. Deno makes the same point on its own page, claiming a 1.5x faster cold start since Deno 2.0.

Measure startup on the machine that runs the workload, not the laptop. A 5 ms start on a developer's machine and a 5 ms start on a throttled container are different numbers, and the container is the one your users pay for.

HTTP throughput: two vendor tests, read honestly

Here the vendors disagree — the most useful fact in this article. Bun's own HTTP server docs publish a benchmark: a server responding "Bun!" to each request, where Bun.serve handles roughly 2.5x more requests per second than Node.js on Linux — about 160,000 requests per second against about 64,000 for Node 16. Deno's homepage publishes a different test: hello-world servers at 100 concurrent connections on an AMD EPYC machine with pinned cores, loaded with oha, median of three runs, uncompressed — Deno.serve at 85,600 requests per second, Bun.serve at 81,900, node:http on Node 26 at 56,300. On Deno's test, Deno leads and Bun's advantage over Node is 1.45x, not 2.5x.

Deno's published benchmark chart: hello-world requests per second Deno 85,600, Bun 81,900, Node 56,300; p99 latency Deno 1.60 ms, Bun 2.29 ms, Node 3.10 ms; peak memory Bun 41 MB, Deno 62 MB, Node 108 MB Excerpt of verbatim figures from deno.com, accessed 14 November 2026 (100 concurrent connections, oha, median of 3 runs).

Bun docs ratio=160,00064,000=2.50×Deno chart, Deno vs Node=85,60056,300=1.52×,Bun vs Node=81,90056,300=1.45×p99 latency, Deno vs Node=3.10−1.603.10=48.4% lower,Bun vs Node=3.10−2.293.10=26.1% lower\text{Bun docs ratio} = \frac{160{,}000}{64{,}000} = 2.50\times \text{Deno chart, Deno vs Node} = \frac{85{,}600}{56{,}300} = 1.52\times, \qquad \text{Bun vs Node} = \frac{81{,}900}{56{,}300} = 1.45\times \text{p99 latency, Deno vs Node} = \frac{3.10 - 1.60}{3.10} = 48.4\% \text{ lower}, \qquad \text{Bun vs Node} = \frac{3.10 - 2.29}{3.10} = 26.1\% \text{ lower}

Two things to carry. First, hardware and Node version change the answer: Bun's test runs against Node 16, Deno's against Node 26. Second, hello-world throughput measures the HTTP layer, not your application: once a framework and a database answer, the runtime's share of the request shrinks. Where the code runs also shapes what the runtime can do: the Cloudflare Workers vs Vercel Edge Functions playbook covers the edge layer this choice sits on.

Do not quote 2.5x without the sentence that follows it: Bun's own test, Bun's own machine, against Node 16. Deno's own test, on newer hardware against Node 26, gives 1.45x. A ratio without its method is a slogan.

TypeScript support: three different kinds of native

All three runtimes now run TypeScript without a build step, but "native" means something different in each. Bun transpiles .ts and .tsx on execution — its docs list TypeScript and JSX support as a headline feature. Deno is TypeScript-first in the same sense: run .ts files directly, no tsc, no build step, no config.

Node arrived last and most cautiously. Node's Learn pages document type stripping: from v22.18.0, a file containing only erasable TypeScript syntax runs with no flags — node example.ts — while earlier versions need --experimental-strip-types. Type stripping removes annotations, interfaces, type aliases and import type, then runs the remaining JavaScript. Two constraints: Node does not type-check at run time, so tsc still belongs in CI; and the Amaro loader ignores tsconfig.json, so editor settings and runtime behaviour can drift unless the tsconfig matches (TypeScript 5.7+ recommended).

npm compatibility: the wall that fell

Compatibility is where the decision usually gets made, and it is where the last two years changed the most. Node is the reference: every package is written and tested against it first, and its module system is the one the other two emulate. Deno 2 closed most of the gap in one release: drop Deno into a repository with a package.json and node_modules and it runs, mixing npm: imports with native ES modules as you migrate.

Bun's compatibility story is measured, not asserted. Bun 1.4 added 1,517 tests from Node's own test suite, and Bun reports node:http, node:fs, node:cluster, node:timers, node:zlib, node:vm and node:stream pass 97 percent of Node's own tests, while node:events, node:trace_events and node:sqlite pass 100 percent. The remaining 3 percent is where production surprises live: run your own test suite on Bun before committing a service to it. What the runtime ships inside is decided by the Docker vs Podman vs nerdctl container runtime playbook. On install speed, Bun's docs claim up to 30x faster than npm; Deno's homepage measures an 18-package tree warm at Deno 598 ms, Bun 766 ms, npm 3.8 seconds — 6.35x and 4.96x over npm, smaller than the headline but measured on a stated tree.

Memory under load: Bun's quietest win

Throughput gets the headlines; memory decides how many services fit on a box. Bun's 1.4 blog measured peak memory under load — 1,000,000 requests with 64 connections — across frameworks. Bun 1.4 peaked at 120 MB for fastify, 92 MB for Express and 81 MB for node:http, against 156, 145 and 107 MB on Node.js 26; Bun.serve itself peaked at 36 MB. Deno's chart agrees: hello-world peak memory of 41 MB for Bun, 62 MB for Deno, 108 MB for Node — Bun at 38.0 percent of Node's footprint, Deno at 57.4 percent.

Bun 1.4 release blog tables: hello.js startup on Linux 5.1 ms for Bun 1.4 against 27.2 ms for Node.js 26; peak memory under load for fastify 120 MB, Express 92 MB, node:http 81 MB and Bun.serve 36 MB Excerpt of verbatim figures from bun.sh/blog/bun-v1.4, 20 August 2026.

fastify memory saving=156−120156=23.1%,Express saving=145−92145=36.6%node:http saving=107−81107=24.3%,Bun.serve vs node:http=36107=33.6% of Node’s footprint\text{fastify memory saving} = \frac{156 - 120}{156} = 23.1\%, \qquad \text{Express saving} = \frac{145 - 92}{145} = 36.6\% \text{node:http saving} = \frac{107 - 81}{107} = 24.3\%, \qquad \text{Bun.serve vs node:http} = \frac{36}{107} = 33.6\% \text{ of Node's footprint}

Memory is the most portable of Bun's advantages: it shows up in every framework on the same test. For a team running dozens of small services on one host, it compounds without any code change.

Server-count cost math: what throughput is worth

Throughput becomes money only through the server count. Take a sustained 200,000 requests per second. On Bun's docs figures — 160,000 requests per second for Bun.serve, 64,000 for Node — Node needs four servers and Bun needs two. On Deno's chart figures — 85,600 for Deno, 81,900 for Bun, 56,300 for Node — Node needs four, Deno three, Bun three.

Servers at 200,000 req/s, Bun docs figures=⌈200,00064,000⌉=4 (Node),⌈200,000160,000⌉=2 (Bun)Servers at 200,000 req/s, Deno chart figures=⌈200,00056,300⌉=4 (Node),⌈200,00085,600⌉=3 (Deno)Monthly cost at 24 dollars per server=4×24=96 dollars (Node),2×24=48 dollars (Bun)Saving, Bun docs figures=96−48=48 dollars per month=576 dollars per year\text{Servers at 200,000 req/s, Bun docs figures} = \left\lceil \frac{200{,}000}{64{,}000} \right\rceil = 4 \text{ (Node)}, \qquad \left\lceil \frac{200{,}000}{160{,}000} \right\rceil = 2 \text{ (Bun)} \text{Servers at 200,000 req/s, Deno chart figures} = \left\lceil \frac{200{,}000}{56{,}300} \right\rceil = 4 \text{ (Node)}, \qquad \left\lceil \frac{200{,}000}{85{,}600} \right\rceil = 3 \text{ (Deno)} \text{Monthly cost at 24 dollars per server} = 4 \times 24 = 96 \text{ dollars (Node)}, \qquad 2 \times 24 = 48 \text{ dollars (Bun)} \text{Saving, Bun docs figures} = 96 - 48 = 48 \text{ dollars per month} = 576 \text{ dollars per year}

The 24-dollar server price is a stated assumption, not a quoted price — use your own hosting bill. On Deno's figures the saving is 24 dollars a month, 288 dollars a year. Either way the saving is modest; the real return is headroom: Node servers run at 78 percent average load, Bun servers at 63 percent, and headroom absorbs a traffic spike without a page. Run your own margin through the profit calculator first.

Scenario: Meridian Analytics prices a switch

Scenario: Meridian Analytics, twelve developers, runs an API gateway at a sustained 200,000 requests per second. On Bun's figures the gateway needs four Node servers or two Bun servers — 48 dollars a month saved, 576 a year. On Deno's figures it needs four Node servers or three Deno servers — 24 dollars a month, 288 a year. The gateway's real cost is not servers but its three native addons: one has no Bun equivalent, one behaves differently under Deno's permission sandbox, and the third is untested on either. Meridian keeps the gateway on Node 26 LTS, moves its CLI tooling to Bun, where the 5.1 ms startup is felt on every invocation, and pilots Deno for a greenfield service where the sandbox default is a feature, not friction. The throughput saving is small; the addon wall is the constraint.

Operator Boundaries

This playbook is for whoever chooses a team's runtime: the tech lead, the staff engineer, or the founder paying the hosting bill. It is written for teams shipping a JavaScript or TypeScript service with a real production load and a CI pipeline — not a framework guide. Who must avoid a switch: anyone depending on a native addon without a Bun or Deno equivalent; anyone under a compliance requirement for a formal LTS window; anyone whose service runs inside a sandbox-sensitive deployment — test Deno's permission model against every dependency first. A runtime switch is a migration, not a config change — it deserves a plan with a rollback.

Founder & CEO Insight — Enow A. Jovial

"A faster runtime is a cheaper server and a shorter wait, and both are worth having. But the runtime is not the product — the service running on it is. Pick the runtime your dependencies already run on." — Enow A. Jovial, Founder & Chief Executive Officer

The switch, run as a checklist

  1. Time your own workload first. Startup, throughput, p99 latency — slowest machine, three runs each.
  2. List the hard requirements. Native addons, LTS windows, permission models, hosting targets — any one can veto a runtime.
  3. Read the benchmark conditions. Machine, Node version, connections, runs — before quoting any ratio.
  4. Run your test suite on the candidate. Your suite is the verdict.
  5. Price the server count. Use the formulas here with your own volume and hosting bill; do not present the saving as larger than the arithmetic supports.
  6. Price the migration. Engineer-hours times your loaded rate, divided by the monthly saving, is break-even.
  7. Pilot on one surface. Move the smallest service first, keep the old runtime deployable.
  8. Decide on headroom and developer time, not headline ratios.

The decision in advance

Pick the runtime by the constraint your team feels, not the ratio on a benchmark page. Bun for startup speed and memory where its compatibility holds; Deno for TypeScript-first development and a security default that treats every dependency as untrusted; Node where the ecosystem, the LTS window, or a native addon decides. Measure first, pilot small.