✨ Strapi MCP is now Generally Available - let your agents manage your Strapi content ✨

Ecosystem●15 min read

Bun vs Node.js: Performance Benchmarks, Features, and Migration Guide

●April 19, 2026●Updated on September 14, 2026
Bun vs Node.js

Node.js runs TypeScript without a build step, ships a stable test runner, and has an opt-in permission model. Bun ships a runtime, package manager, bundler, test runner, and database clients in one binary, and its 1.4.0 release rewrote the runtime from Zig to Rust. The Bun vs Node.js question now hinges on which trade-offs your production stack can absorb: observability agents, native addons, memory under load, sandboxing, and the platforms you deploy to.

This guide is for full-stack developers and technical leads picking a runtime for a new TypeScript service, a CI pipeline, or a frontend that talks to a headless CMS backend. It covers current benchmark evidence (including where Bun loses), the security posture of each runtime after the 2025–2026 npm supply-chain attacks, framework and addon compatibility, a phased migration path, and what Strapi officially supports.

In brief:

  • Bun leads synthetic HTTP tests (48,243 vs 25,181 req/s on Bun's own benchmark), but a 2025 peer-reviewed study measured 27,500 req/s for Bun and 28,200 for Node once the comparison moved beyond a vendor benchmark.
  • Bun 1.4 passes 97% of the node:http, node:fs, and node:stream test suites, yet Fastify, NestJS, Datadog, New Relic, and OpenTelemetry do not officially support it.
  • Node.js has an opt-in --permission flag that its own docs say does not stop malicious code; Bun has no runtime sandbox, and its "Secure Mode" pull request was closed in June 2026.
  • Strapi 5 runs on Node.js 22, 24, or 26 and cannot run on Bun as a server runtime; the recommended pattern is Node.js for Strapi and Bun for the frontend and tooling.

What Changed in Node.js, Bun, and Deno

Node.js: Native TypeScript and a Renamed Permission Flag

Node.js has three supported lines: 26.8.2 (Current), 24.21.0 (Active LTS, codenamed Krypton), and 22.23.2 (Maintenance LTS). Version 25 reached end of life on March 31, 2026 (Node.js release schedule). Type stripping reached Stability 2 in 24.12.0 and 25.2.0, so node file.ts works on 24 and later without a flag.

Node replaces erasable syntax (annotations, interfaces, generics) with whitespace; it does not read tsconfig.json, emit source maps, or type-check (Node.js TypeScript docs). The --experimental-transform-types flag that older guides recommend for enums and namespaces was removed in 26.0.0.

node:test has been Stability 2 since 20.0.0. The Permission Model reached Stability 2 in 22.13.0, the flag was renamed from --experimental-permission to --permission in 24.0.0, and 25.0.0 added --allow-net.

Bun: One Binary, Built-In Clients, and a Rust Rewrite

Bun 1.4.2 is the current stable release. Bun 1.4.0 (August 2026) rewrote the runtime in Rust and added 1,517 tests from the Node.js suite (Bun 1.4 release). The binary includes bun:sqlite (since 1.0), Bun.sql for Postgres and an S3 client (since 1.2, January 2025), a Redis client (since 1.2.9), and MySQL/MariaDB support in Bun.sql (since 1.2.21). The text lockfile bun.lock became the default in 1.2; a Dockerfile still copying bun.lockb is behind.

Anthropic announced its acquisition of Bun on December 3, 2025 and stated that "Bun will remain open source and MIT-licensed." The license page still reads MIT. Windows x64 has been stable since 1.1.0 and Windows ARM64 landed in 1.3.10.

Deno: Deny-By-Default Permissions

Deno 2.9.6 is current. Programs start with zero default permissions for file, network, and environment access. Deno 2.5 added named permission sets in deno.json, Deno 2.6 added deno approve-scripts and the dx command, and Deno 2.8 added a permission audit log through OpenTelemetry.

Over 75% of Node's own test suite passes as of 2.8 (Deno Node compatibility). Deno Sandbox, the microVM product, launched in beta in February 2026 as a paid Deno Deploy service capped at five concurrent sandboxes per organization during pre-release (Deno Sandbox docs). Pick Deno when running untrusted code is the design requirement. The rest of this article stays on Bun and Node.js.

Bun vs Node.js Performance Benchmarks

HTTP Throughput: Synthetic Gap, Production Parity

Bun's homepage benchmark (Bun 1.4 vs Node 26.7.0, Linux x64 EPYC 9R14, bombardier) reports 48,243 vs 25,181 req/s. Bun's own benchmarking guide warns that autocannon can be too slow to saturate Bun and recommends bombardier, oha, or http_load_test.

Real applications can erase or reverse the gap. A 2025 peer-reviewed study in JCSI measured I/O-bound throughput at 27,500 req/s for Bun and 28,200 for Node.

Bun can also lose. A Bun GitHub investigation measured 36,887 vs 26,050 req/s on hello-world, then 11,800 vs 16,200 req/s under allocation-heavy load; PR #35356 cut the penalty from −33% to −8%. If your hot path churns objects, run your own load test before assuming a Bun win.

CPU-Bound Work and Memory

Current results depend on the operation: the JCSI study measured recursive Fibonacci(40) at 561.5 ms on Bun 1.1.43 vs 749.8 ms on Node 22.13.0, and a June 2026 issue found Bun 1.4.0 1.53× faster than Node 26.3.0 on JSON.stringify of 5,000 objects but slower on heavily escaped strings.

Memory can also favor Node. On framework-heavy workloads Bun uses more: a Next.js standalone server on Bun 1.4.0 sat at 179 MB after startup and 318 MB after 45 seconds idle, against 80 MB on Node 24 (issue #34389), and the JCSI study also recorded higher memory for Bun during HTTP tests.

Package Install Speed: Bun's Most Consistent Win

Bun's published benchmark (Bun 1.4, npm 12.0.2, pnpm 11.21.0, Yarn 1.22.22, T3-stack fixture of about 220 packages) measures a first install at 1.41 s for Bun, 13.49 s for pnpm, 18.12 s for npm, and 20.51 s for Yarn; with a warm CI cache, Bun takes 210 ms against pnpm's 1.92 s. That is a vendor benchmark. pnpm's own benchmarks omit Bun entirely.

You can take this win without changing runtimes. Per the bun install docs, bun install "installs a Node.js-compatible node_modules folder" that works "in place of npm install for Node.js projects without any code changes and without using Bun's runtime."

Serverless Cold Starts: Benchmark Your Deployment Path

AWS Lambda offers managed nodejs26.x, nodejs24.x, and nodejs22.x runtimes and no managed Bun runtime; Bun runs there only through the bun-lambda custom layer or a container image. Because these deployment paths differ, cold-start performance should be measured with your exact package, architecture, memory allocation, and traffic pattern rather than inferred from a runtime-only comparison.

JavaScriptCore's top FTL tier engages after roughly 100,000 execution points, while V8's Maglev compiles about 10× faster than TurboFan and optimizes earlier in a function's life.

Platforms that run Bun as a first-class runtime: Vercel (bunVersion: "1.x" in vercel.json), Railway, and Render. Netlify Functions, Cloudflare Workers, Google Cloud Run, and Azure Functions execute on Node.js or V8 and use Bun, at most, as a build tool.

TypeScript Execution and Built-In Tooling

Bun runs .ts files with full syntax support (enums, namespaces, decorators) and no configuration; Node strips erasable syntax only. Neither affects a Strapi project, which compiles its own src/ folder through the Strapi TypeScript setup (pass --typescript at creation or convert an existing project). If your Strapi build fails on a type error, compiling despite type errors is a separate tsconfig question.

A React project using Node.js TypeScript support can combine npm, TypeScript, Vite, and Jest or Vitest, each with its own config; the built-in node --test covers mocking, snapshots, and --experimental-test-coverage, but bundling still needs a separate tool. Bun folds those capabilities into its CLI through bun init, bun run dev, bun build, and bun test, and its database clients remove dependencies outright:

// Built-in Postgres — no npm package needed
import { sql } from "bun";
const users = await sql`SELECT * FROM users WHERE active = ${true}`;

// Built-in Redis
import { redis } from "bun";
await redis.set("session", JSON.stringify(sessionData));

Bun.serve() uses Fetch API Request and Response objects and has had a declarative routes option since 1.2.3. HTTP/2 arrived directly in Bun.serve in 1.4.1 as experimental (http2: true), and server.upgrade() for WebSockets "only works on HTTP/1.1 requests" (Bun HTTP server docs).

Security: Permission Model vs Full Trust

The threat is concrete. On September 8, 2025, a phished maintainer account pushed malicious chalk@5.6.1 and debug@4.4.2 that redirected cryptocurrency transactions in browser apps. A CISA alert on the Shai-Hulud worm counted over 500 compromised packages. In 2026, axios@1.14.1 shipped a remote-access trojan, 42 @tanstack/* packages were compromised through GitHub Actions OIDC binding, and the ChainDrop campaign touched 1,300+ package versions including keyv.

Install-time defaults differ. Bun's package manager runs lifecycle scripts only for packages on an allow list, extended through trustedDependencies (Bun lifecycle docs). Deno blocks them until you run deno approve-scripts.

At runtime, Node offers a process-level allow list:

node --permission --allow-fs-read=/data --allow-net=api.example.com app.js

Node's own permissions documentation states the model "does not protect against malicious code" and lists bypasses: worker threads don't inherit restrictions, existing file descriptors sidestep node:fs, and --env-file reads before the model initializes. Node patched Permission Model bypasses in security releases in March 2026 (including CVE-2026-21716, a patch bypass of CVE-2024-36137), June 2026, and July 2026. Treat it as defense in depth.

Bun has no runtime permission system. PR #25911 ("Secure Mode," with CLI flags and a Bun.permissions API) was closed on June 26, 2026, and issue #26637 was closed as a duplicate on August 13, 2026. With Bun, dependency scanning, lockfile pinning, and CI policy remain important defenses outside the runtime. Strapi includes two separately configured access-control systems: Admin Panel RBAC governs administrator access, while the Users and Permissions capability handles application-user authentication and authorization through API roles and permissions. Both are built into the Node.js Strapi backend and require role and permission configuration; using Bun for a separate frontend or tooling layer does not change those backend controls.

Node.js Compatibility, Native Addons, and Frameworks

Bun publishes module-level pass rates rather than one overall number. Bun 1.4 reports 100% for node:events and node:sqlite, 97% for node:http, node:fs, node:stream, node:cluster, node:zlib, and node:vm, and 94% for node:http2. The compatibility docs target Node.js 23 as the baseline.

Native addons are the main breakage point. Bun implements Node-API from scratch, covering all 156 functions, and Bun 1.3 reported 98%+ of Node's N-API tests passing (Bun 1.3 release). Modules that link directly against V8 will never work, and async_hooks is stubbed with IDs always 0 (OpenTelemetry issue #5260).

PackageStatus on BunReplacement
bcryptUnreliable; napi_register_module_v1 not found on Windows (#23136)Bun.password (bcrypt, argon2id)
argon2Fails with libstdc++.so.6 error on Linux (node-argon2 #490)Bun.password
better-sqlite3Not supported; uses V8 C++ APIs (#4290)bun:sqlite, different API
sharpOfficially supports Bun; segfault (#20372) and install regression (#24550) reportedNone needed
canvasVersion 2 unsupported; canvas@next conditional, fails on Windows (#5835)Alternative required

Framework support splits cleanly. Hono documents Bun as a first-class target, and Elysia is "optimized for Bun, with runtime support for Node.js" through @elysia/node. Elysia's own table lists Elysia on Bun at 255,574 req/s, Hono on Bun at 203,937, Express on Bun at 29,715, and Express on Node at 15,913. Next.js accepts bun --bun next dev but still lists Node.js 20.9+ as a requirement. Fastify states: "This project targets the Node.js runtime. If you are encountering issues using Bun, please file issues there." NestJS maintainers say "We're focused on node." If you're weighing frameworks, the State of JS 2025 takeaways cover where Hono and Elysia sit, and the Express and Fastify guides cover the Node side.

Observability is the quiet blocker. Datadog's dd-trace has no official Bun support (maintainer, February 2026: "While we currently can not yet add official support, we will likely work on community PRs"), New Relic closed its Bun issue as "won't be actioned," and OpenTelemetry JS maintainers wrote that "making our tests run against bun is quite difficult." Bun 1.4 claims OTel's http and fs instrumentation work; that is Bun's claim, not the OTel project's. Debugging has caveats too: Bun's VS Code guide labels the extension "buggy" and recommends the web debugger at debug.bun.sh, and a line-skew bug under --inspect-brk was reproduced in Bun 1.3.14 (#32591).

Bun vs Node.js Comparison Table

FactorNode.jsBun
Engine and implementationV8JavaScriptCore; runtime in Rust since 1.4.0
Package managernpm, pnpm, Yarn (external)Built-in bun install
Bundleresbuild, Rollup, Webpack (external)Built-in bun build
Test runnernode:test, or Jest/VitestBuilt-in Jest-compatible bun test
TypeScriptType stripping, erasable syntax onlyFull syntax, zero config
Built-in database clientsNoneSQLite, Postgres and S3, MySQL/MariaDB, Redis
Runtime sandboxOpt-in --permission, documented bypassesNone
Install-time lifecycle scriptsRun by defaultAllow list
Managed AWS Lambda runtimeYesNo
Official APM vendor supportDatadog, New Relic, OpenTelemetryNone
Smallest official Docker imagenode:24-alpine 55.78 MBoven/bun:alpine 40.33 MB
GovernanceOpenJS FoundationAnthropic, MIT license

When to Choose Node.js, Bun, or Both

Choose Node.js When

  • The service depends on Fastify, NestJS, better-sqlite3, or argon2.
  • You need vendor-supported tracing from Datadog, New Relic, or OpenTelemetry auto-instrumentation.
  • You deploy to AWS Lambda, Netlify Functions, Cloudflare Workers, Cloud Run, or Azure Functions.
  • You run Strapi (see the section below).
  • Some organizational policies require an LTS schedule or a runtime-level permission flag, even a porous one.

Choose Bun When

  • You're starting a TypeScript service on Hono or Elysia and deploying to Vercel, Railway, Render, or your own containers.
  • Built-in Postgres, S3, and Redis clients can let you remove corresponding dependencies, potentially reducing the supply-chain surface.
  • Install time dominates CI, or developer onboarding waits on npm install.
  • A simple-server benchmark found lower memory use for Bun, but you should load-test allocation-heavy paths and your actual framework yourself.

Build with Bun, Run with Node.js

A practical pattern is to use Bun for installs, tests, and bundling while keeping Node for the production process. Bun's bundler docs confirm target: 'node' output runs on both runtimes.

// package.json
{
  "scripts": {
    "dev": "bun --watch src/index.ts",
    "test": "bun test",
    "build": "bun build src/index.tsx --outfile dist/app.mjs --target node",
    "start": "node dist/app.mjs"
  }
}
# Dockerfile
# Build stage: Bun for dependency installation
FROM oven/bun:1.4-alpine AS dependencies
WORKDIR /app
COPY package.json bun.lock ./
RUN bun install --frozen-lockfile --production

# Production stage: Node.js runtime
FROM node:24-alpine AS production
WORKDIR /app
COPY --from=dependencies /app/node_modules ./node_modules
COPY . .
CMD ["node", "dist/app.mjs"]

Pin a patch tag such as oven/bun:1.4.2-alpine for reproducible builds. In GitHub Actions, oven-sh/setup-bun@v2 (2.2.0) reads a .bun-version file through bun-version-file. Commit one lockfile format, not both; bun install --frozen-lockfile failures in CI have their own issue history (#19088, #26973).

Migrating a Node.js Project to Bun

Audit native addons first. Run find node_modules -name "*.node" -type f and check the output against the compatibility table above. Anything V8-linked stays on Node.

Install and run the suite. Run bun install followed by bun test. Bun's runner is Jest-compatible: describe, it, expect, and spyOn work unchanged; import mock from bun:test in place of jest.fn() and jest.mock().

Run both runtimes in CI, then shadow-deploy. Add a test-bun job alongside test-node (actions/setup-node@v4 with node-version: '26'). For production, deploy identical images, split traffic behind the load balancer, and compare error rates, memory, and latency against the Node baseline over weeks rather than hours. Keep the Node image deployable for rollback.

Before switching production to Bun, confirm:

  • Every .node module identified and replaced or isolated
  • bun test green
  • Database connection pooling and WebSocket upgrades tested (HTTP/1.1 only for upgrades)
  • Your observability vendor's Bun position confirmed in writing
  • Bun version pinned in .bun-version and the Docker tag
  • Rollback rehearsed

Running Strapi with Node.js and Bun

The current Strapi 5 CLI support matrix lists Node.js 22, 24, and 26, with npm 6+, pnpm via Corepack, or Yarn via --use-yarn (CLI installation docs). There is no --use-bun flag. Bun cannot run Strapi 5 as a server runtime; the official Strapi Bun integration page says so directly, and a core maintainer explained on the feedback forum that Bun "was throwing errors due to some of the way koa-router works and the way the ctx is handled." bun install works for a Strapi project with a caveat: it may not respect subdependency aliases (GitHub issue #5868).

The supported setup is the hybrid one described above: Node.js runs the Strapi backend, and Bun runs your frontend (the integration page uses Astro as its example) and tooling. A Next.js frontend consuming Strapi's REST API (REST vs GraphQL), which is enabled by default with all content types private until you open them, or the GraphQL plugin (installed separately as @strapi/plugin-graphql) has no runtime dependency on the Strapi process at all. Which API to expose is its own decision; the Strapi 5 API comparison walks through it. The REST API documentation covers the default option.

For containers, Strapi's Docker guide states that "Strapi does not build any official container images"; its community-maintained examples use node:22-alpine and npm in both build and production stages, and 24 and 26 are also listed as supported by the current CLI support matrix. Databases are configured in the database configuration: PostgreSQL 14+, MySQL 8.0+, MariaDB 10.3+, or SQLite 3. If you self-host, the deployment guides cover Nginx, Caddy, Traefik, and PM2; the self-hosted vs Strapi Cloud comparison covers the alternative, with the managed hosting options detailed in the Strapi Cloud deployment documentation. Once the runtime is settled, performance work in Strapi happens in query shape, population depth, and caching configured separately at the CDN or hosting layer rather than supplied automatically by a self-hosted Strapi application, none of which a faster runtime changes.

Start with Install Speed, Then Decide on Runtime

If your bottleneck is CI time, switch npm ci to bun install this week and leave the runtime alone; the install benchmarks stand on their own. If you're starting a Hono or Elysia service on Vercel or Railway, run it on Bun and load-test your allocation-heavy endpoints before launch. If you run Strapi, keep it on Node.js 24 or 26, use Bun for the frontend build, and manage the two-runtime setup with a multi-environment workflow so staging exercises the same split as production.

Paul BratslavskyDeveloper Advocate
The True TCO of Self-Hosting a Headless CMS
Ecosystem·12 min read

The True TCO of Self-Hosting a Headless CMS

Discover the real cost of self-hosting Strapi 5: infrastructure, DevOps labor, security, migrations, and hidden costs—with a practical TCO checklist.

·September 24, 2026
How Do You Preserve SEO Rankings During a CMS Migration?
Ecosystem·13 min read

How Do You Preserve SEO Rankings During a CMS Migration?

A developer's CMS migration SEO checklist: URL mapping, 301 redirects, on-page parity, robots.txt, redirect testing, and post-launch monitoring.

·September 24, 2026
What Is a Content Agent? Agentic AI for CMS Workflows Explained
Ecosystem·24 min read

What Is a Content Agent? Agentic AI for CMS Workflows Explained

Learn what a content agent is, how the observe-plan-act-evaluate loop works, and when agentic AI makes sense for your CMS workflows and content operations.

·September 24, 2026