Why We Run Production APIs on Bun 1.1 — 2.3× Throughput, Half the Memory, But We Keep Node for Builds

Bun 1.1 handles 18k req/s (vs 7.8k Node 20) on our tRPC API with 45MB RSS (vs 120MB Node) — but `bun build` still has ESM/CJS interop bugs and Vite 6+ requires Node. Hybrid: Bun for runtime, Node for CI/build.

Published 2026-06-17

Why We Run Production APIs on Bun 1.1 — 2.3× Throughput, Half the Memory, But We Keep Node for Builds

TL;DR: Bun 1.1 delivers 18k req/s on our tRPC API (vs 7.8k Node 20) at 45MB RSS (vs 120MB) — but bun build fails on our monorepo’s ESM/CJS boundary and Vite 6 requires Node. We run production API on Bun; CI/builds on Node 20. Full comparison →

The Context

Three-dev team, Next.js 14 (web) + tRPC/Express (API) + PostgreSQL. Node 20 LTS since 2023. API pain points: p99 180ms under load, 120MB RSS per worker (4 workers = 480MB), cold start 1.2s. Evaluated Bun 1.1 (stable May 2026) for API runtime June 2026.

What We Tested

RuntimeUse CaseVerdictWhy
Bun 1.1 (Linux x64)Production tRPC API (Express adapter)18k req/s (wrk 10s, 100 conn), 45MB RSS, 0.3s cold start, native SQLite/Postgres
Node 20.18 (LTS)Same API, cluster mode (4 workers)⚠️7.8k req/s, 120MB RSS/worker, 1.2s cold start, mature ecosystem
Bun 1.1Next.js 14 build (bun run build)ESM/CJS interop fails on @trpc/next + next-auth; Vite 6 requires Node
Node 20.18Next.js build + Vite + TurborepoWorks reliably; 2.1x slower than bun build on clean projects

The Pivot Point

June 3: bun run start:api on staging — 18k req/s, 45MB RSS, zero ENOMEM crashes (had 2/month on Node). June 5: Tried bun run build in CI for Next.js web — Error: Cannot find module '@trpc/next' (ESM/CJS boundary in monorepo). bunx vite buildVite 6 requires Node.js 20.19+. June 7: Hybrid approach locked in. API Dockerfile: FROM oven/bun:1.1bun run start:api. Web Dockerfile: FROM node:20-alpinepnpm build. CI: bun install (3.2× faster than pnpm install) → Node for builds.

What We Use Now

Bun 1.1 for production API runtime:

  • Dockerfile: FROM oven/bun:1.1-alpineCOPY package.json bun.lockb ./bun install --productionCMD ["bun", "run", "start:api"]
  • API entry: src/api/index.tsimport { serve } from "bun"; serve({ fetch: app.fetch, port: 3001 }) (Bun.serve native, no Express overhead)
  • Database: bun:sqlite for local dev, postgres (pg) for production — both native, no node:fs polyfills
  • Observability: bun --inspect for debugging, bun:jsc for heap snapshots

Node 20 LTS for builds, CI, and web runtime:

  • Dockerfile: FROM node:20-alpinepnpm install --frozen-lockfilepnpm buildpnpm start
  • Turborepo: turbo run build — Node required for @vercel/nft, next-swc, sharp
  • CI: bun install (workspaces, 3.2× pnpm) → node .turbo/build.js (Turborepo daemon)

When You’d Choose Differently

  • Node 20/22 LTS if: you need maximum ecosystem compatibility, rely on native addons (sharp, bcrypt, canvas), or use Vite 6+ / Next.js 15+ which target Node APIs — Bun’s Node compat is ~95% but the 5% breaks builds.
  • Deno 2.0 if: you want secure-by-default, built-in TypeScript, JSR registry, and web-standard APIs — but slower startup than Bun, smaller ecosystem.
  • Bun only if: greenfield project, no complex monorepo ESM/CJS boundaries, can use Bun-native tooling (bun test, bun build for simple libs) — best DX when it works.

Tool Crucible Rating

DimensionRating (1–5)Notes
Overall4Runtime performance exceptional; build tooling gaps force hybrid
Ease of Use4Drop-in for node on API; bun install is magic; build failures are sharp edges
Value5Free, MIT, 2.3× throughput = 2.3× less compute cost
Support3Active Discord, weekly releases, but Node compat bugs depend on ecosystem

This is part of our Runtime Evaluation series. See full comparison: Bun 1.1 vs Node 20 vs Deno 2.0: Production Runtime Benchmarks 2026

Last reviewed 2026-06-17. See our methodology and affiliate policy.