Skip to content

Split View: Bun 회고 2026 — 1.0 GA의 약속 vs 현실, 런타임·번들러·패키지 매니저·테스트 러너·셸까지 (Bun 2 시점에서 본 솔직한 평가)

|

Bun 회고 2026 — 1.0 GA의 약속 vs 현실, 런타임·번들러·패키지 매니저·테스트 러너·셸까지 (Bun 2 시점에서 본 솔직한 평가)

프롤로그 — 2023년의 약속, 2026년의 청구서

2023년 9월, Bun 1.0이 GA로 나왔을 때의 분위기를 기억한다. 데모는 인상적이었다. bun installnpm install보다 25배 빨랐고, bun test는 Jest보다 13배 빨랐고, bun run은 Node보다 4배 빨랐다. Twitter에는 그 차트들이 도배됐다. "이제 Node는 끝났다"는 의견과 "또 다른 Yarn 1.0 모먼트일 뿐"이라는 의견이 반반이었다.

2026년 5월. 청구서를 받을 시간이다.

Bun은 약속을 절반쯤 지켰다. 그러나 그 절반은 우리가 기대했던 절반과는 다른 절반이다.

bun install은 정말 빠르다 — 약속대로. bun test도 빠르다. Bun.serve도 진짜로 가볍고 빠르다. 그러나 운영 환경에서 Node를 대체했다는 시나리오는 흔하지 않다. 2026년 중반의 진짜 그림은 이렇다:

  • 개발 도구로서의 Bun은 광범위하게 쓰인다 — bun install, bun test, Bun shell. CI 시간이 절반으로 준다.
  • 런타임으로서의 Bun은 여전히 niche다 — Bun.serve로 만든 작은 API 게이트웨이, Edge 워커, 내부 도구. 대규모 모놀리스가 Bun으로 갈아탔다는 얘기는 거의 없다.
  • Node 22+가 의외로 잘했다node --experimental-strip-types, node:test, node --watch, native fetch. Node가 Bun이 던진 공을 받아서 자기 코트에서 답했다.

이 글은 1.0 GA 이후 2년 반을 honest retrospective로 정리한다. 무엇이 over-deliver했고, 무엇이 broken promise였고, 어느 자리에 Bun이 맞고 어느 자리에 Node가 여전히 옳은지.


1장 · Bun 런타임 — Zig + JavaScriptCore의 약속

약속

Bun은 시작부터 세 가지 베팅을 했다.

  1. Zig로 다시 쓴다 — V8 위에서 도는 Node와 달리, 런타임 자체를 Zig로 작성해 시작 시간과 메모리 풋프린트를 줄인다.
  2. V8이 아니라 JavaScriptCore(JSC) — Safari가 쓰는 엔진. 시작 시간과 메모리에서 V8보다 가볍다는 일반적인 평가.
  3. Node API 호환fs, http, path, crypto 등 Node의 모듈을 그대로 지원한다. 즉, "Node 코드를 그대로 갖다 붙여도 돈다"는 약속.

이 세 가지 조합으로 Bun은 "Node를 갈아낼 수 있는 드롭인 대체"를 노렸다.

현실 — 런타임 속도

벤치마크는 일관되게 Bun이 Node보다 빠르다고 말한다. 그러나 어디서 빠른가가 중요하다.

시작 시간 (cold start, "console.log"):
  Node 22:  35~60ms
  Bun 1.2:  10~20ms
  Deno 2:   25~40ms

HTTP 처리량 (단순 에코, single core):
  Node 22 (http):      80k req/s
  Node 22 (uWebSockets) 280k req/s
  Bun.serve:           250~310k req/s
  Hono on Bun:         220~280k req/s
  Deno serve:          200~240k req/s

SQLite 쿼리 (1M rows, simple select):
  better-sqlite3 (Node): 4.2s
  bun:sqlite:            1.6s

빠르긴 하다. 그런데 차이의 모양을 보자.

  • 시작 시간 2~3x: AWS Lambda 같은 cold-start 민감 워크로드에서 의미 있다. 일반 long-running 서버에서는 무의미하다.
  • HTTP 처리량: Node가 기본 http 모듈을 쓰지 않고 uWebSockets나 fastify를 쓰면 그 차이가 크게 좁아진다. 공정한 비교는 "기본 vs 기본"이 아니라 "최적화된 vs 최적화된"이다.
  • SQLite: 진짜 차이가 큰 영역. bun:sqlite는 FFI로 SQLite를 직접 호출해서 N+1을 깎는 구조. 이건 분명한 win.

핵심: Bun이 빠른 이유는 "JS 실행이 빠르다"가 아니라 "I/O와 native 인터페이스가 가볍다"는 쪽이다. 그래서 CPU-bound JS 코드에서는 Node와 큰 차이가 없는 경우가 많다.

현실 — Node 호환성

Bun은 1.0 시점에 "Node 호환률 90%+"를 광고했다. 2026년 현재의 체감은 이렇다.

영역호환 상태사용 가능?
fs, path, os, util거의 완벽
http, https, url거의 완벽
crypto대부분 OK, 일부 legacy API에서 차이
child_process대부분 OK
worker_threadsOK
cluster부분 지원, edge case에서 차이주의
Native addons (N-API)광범위 지원, 일부 미지원 모듈 존재예 (대부분)
vm 모듈일부 누락 (특정 옵션)주의
inspector (debugger 프로토콜)부분 지원주의
async_hooks부분 지원 — APM 도구 호환 이슈주의
process.binding 내부 API미지원 (의도적)아니오

작은 서비스에서는 거의 문제가 없다. 그러나 APM(Application Performance Monitoring) 에이전트, OpenTelemetry auto-instrumentation, legacy native addon 같은 곳에서 여전히 깨진다. New Relic, Datadog 등 메이저 APM은 2025년 말이 되어서야 Bun을 공식 지원했고, 일부 instrumentation은 여전히 partial이다.

"Node 코드를 그대로 옮겨도 돈다"는 작은 코드베이스에서는 사실이고, 큰 코드베이스에서는 절반쯤 사실이다. 깨지는 것은 비즈니스 코드가 아니라 **주변 인프라(tracing, profiler, debugger)**다.


2장 · bun install — 가장 명확한 win

여러 영역 중 가장 의심의 여지가 적은 winbun install이다.

벤치마크

hot cache, mid-size repo (약 800개 deps):
  npm install:    18s
  yarn install:    9s
  pnpm install:    5s
  bun install:     1.6s

cold cache, 같은 repo:
  npm install:    65s
  yarn install:   35s
  pnpm install:   18s
  bun install:    7s

bun install은 Rust로 쓰인 cargo나 Go의 module resolver 수준의 속도를 JS 생태계에 가져왔다. 이건 dev 경험의 진짜 차이다 — CI에서 bun install은 매번 5~10초로 끝나고, 로컬에서 lockfile이 변경됐을 때 다시 깔리는 시간이 사람의 인내심을 시험하지 않는다.

lockfile 전쟁의 짧은 역사

처음 1.0이 나왔을 때 bun.lockb바이너리 lockfile이었다. 빠른 파싱이 목표였지만, PR 리뷰에서 diff를 볼 수 없다는 큰 문제가 있었다. 2024~2025년에 걸쳐 커뮤니티가 강하게 항의했고, Bun은 결국 bun.lock(텍스트, JSON-superset 형식)을 도입했다. 2026년 현재, 새 프로젝트는 텍스트 lockfile이 디폴트다. 옛 프로젝트는 bun.lockb를 그대로 쓰는 경우도 있다.

교훈: DX 베팅에서 한쪽 면(속도)만 보고 다른 면(diff 가능성, code review)을 무시하면 커뮤니티가 돌려보낸다. Bun 팀은 이걸 인정하고 빠르게 수정했고, 그건 평가받을 만하다.

어디서 흔히 쓰이나

bun install런타임을 Bun으로 바꾸지 않고도 도입할 수 있다. CI에서 bun install + npm run build/node server.js 조합이 매우 흔하다.

  • Vercel·Netlify 같은 platform은 bun install을 빌드 단계에서 옵션으로 제공.
  • Next.js, SvelteKit, Astro 등 메이저 프레임워크는 bun install로 dep을 깔아도 정상 빌드.
  • Docker 이미지에서 oven/bun 베이스 + Node runtime 조합으로 빌드 시간 절반.

가장 안전한 first-step Bun 도입 — 그리고 가장 흔한 도입 — 이 bun install이다.


3장 · bun test — 빠르지만 채택률은 mixed

약속

bun testJest API 호환(혹은 가까운)을 표방하며, Jest보다 훨씬 빠르고, transform/transpile 설정 없이 TypeScript를 그대로 돌린다.

현실

벤치마크상 Bun test는 Jest보다 310x 빠르다. Vitest와는 13x 차이 (Vitest가 빠를 때도 있고 Bun이 빠를 때도 있음, suite 모양에 따라). 그러나 채택률은 mixed다.

도구2026 mid 채택률 (느낌)강점
Jest여전히 1위 (legacy 무게가 큼)생태계, 플러그인, 안정성
Vitest빠르게 2위 추월 중Vite 통합, ESM-native, watch UX
Bun test3위, 신규 프로젝트에 흔함속도, 무설정 TS
Node test (node:test)점점 늘어남dependency 없음, official

Bun test가 메인 stack을 차지하지 못한 이유는 몇 가지다.

  1. Jest의 plugin/ecosystem: snapshot serializer, custom matchers, jest-axe, jest-extended 등 수많은 플러그인. Bun test에 다 옮겨오기까지 시간이 걸렸다.
  2. mock 시스템의 차이: jest.mock() 호환은 점점 좋아지고 있지만 edge case에서 차이가 있다.
  3. monorepo 통합: Turborepo·Nx 같은 monorepo 도구의 Jest/Vitest 통합이 더 매끄럽다.

Bun test는 신규 단일 패키지 프로젝트에서 가장 자주 쓰인다. 큰 monorepo에서는 여전히 Vitest 우세.


4장 · Bun bundler — esbuild의 자리에 도전했지만

약속

Bun에는 내장 bundler가 있다 — bun build. esbuild에 영감을 받았다고 공언하고, 더 빠르다고 광고했다.

현실 — bundler 풍경 2026

2026년의 JS 번들러 풍경은 1.0 시점보다 훨씬 복잡해졌다.

번들러위치특징
esbuild여전히 dominant가장 작은 표면적, 가장 안정
Vite (Rollup)dev server 표준HMR, plugin 생태계
RolldownVite의 다음 단계Rust 기반, esbuild보다 빠를 수 있음
TurbopackNext.js의 defaultVercel 베팅, Webpack 후속
Bun bundlerniche, 주로 Bun 생태계 안속도, 무설정 TS, Bun runtime과 통합
swcpackswc의 bundler 분기Vercel 전 generation

Bun bundler는 빠르긴 빠르다. 그러나 plugin 생태계가 esbuild나 Rollup보다 한참 작다. PostCSS·SVGR·MDX 같은 영역에서 esbuild plugin을 그대로 못 쓰는 경우가 많다 (Bun이 esbuild의 plugin API를 호환하려 노력하긴 한다).

Bun bundler는 Bun 생태계 안의 default로는 합리적이다. Bun.serve로 만든 작은 SPA, Bun-only CLI 도구의 dist. 그러나 esbuild를 대체하는 industry-wide standard로 자리잡지는 못했다. 그 자리는 Rolldown이 노리고 있다.


5장 · Bun.serve() — 웹 프레임워크 자리의 싸움

약속

Bun.serve는 "express 없이도 빠르게 HTTP 서버 짤 수 있다"는 약속이다. Fastify 수준의 처리량을 표준 라이브러리에서.

코드 — 30초로 보는 Bun.serve

// server.ts
import { serve } from "bun";

serve({
  port: 3000,
  async fetch(req) {
    const url = new URL(req.url);
    if (url.pathname === "/hello") {
      return new Response("hi");
    }
    if (url.pathname === "/echo" && req.method === "POST") {
      const body = await req.json();
      return Response.json({ ok: true, body });
    }
    return new Response("not found", { status: 404 });
  },
});

console.log("listening on http://localhost:3000");

Web standard Request/Response API를 쓰는 게 핵심이다. Cloudflare Workers·Deno와 같은 패러다임 — Bun을 거기 줄 세웠다.

현실 — Hono의 부상

Bun.serve는 빠르다. 그러나 그 위에 router·middleware·validation을 얹어야 진짜 앱이 된다. 2026년의 사실상의 표준은 Hono다.

// hono on bun
import { Hono } from "hono";

const app = new Hono();
app.get("/hello", (c) => c.text("hi"));
app.post("/users", async (c) => {
  const body = await c.req.json();
  return c.json({ id: 1, ...body });
});

export default { fetch: app.fetch, port: 3000 };

Hono는 Bun, Deno, Cloudflare Workers, Vercel Edge, Node 모두에서 같은 코드로 돈다. 즉, Hono를 쓰면 runtime 선택을 미루거나 바꿀 수 있다. 이게 큰 셀링 포인트다. Bun.serve에 직접 묶이는 코드보다 Hono+Bun 조합이 운영적으로 더 안전하다.

Bun.serve는 좋다. 그러나 그 위에서 실제로 빌드할 때 사람들이 Hono를 더 자주 고른다. Bun은 Hono를 부정하지 않는다 — Hono를 Bun.serve의 정식 라우터로 추천하는 분위기.


6장 · bun:sqlite — 가장 over-deliver한 것

여러 기능 중 가장 기대 이상으로 좋았던 것을 하나만 꼽으라면 bun:sqlite다.

왜 좋은가

Node에서 SQLite를 쓰려면 better-sqlite3(native addon) 또는 sqlite3(async)를 깐다. 둘 다 설치 단계에서 native build를 거치고, 플랫폼 간 호환성 문제가 종종 생긴다. bun:sqlite런타임이 SQLite를 직접 가지고 있다 — 설치 단계 build 없음, SDK처럼 import해서 바로 쓴다.

코드

// sqlite.ts
import { Database } from "bun:sqlite";

const db = new Database("app.db");
db.run(`
  CREATE TABLE IF NOT EXISTS posts (
    id INTEGER PRIMARY KEY,
    title TEXT NOT NULL,
    created_at INTEGER NOT NULL
  )
`);

const insert = db.prepare(
  "INSERT INTO posts (title, created_at) VALUES (?, ?)"
);
const tx = db.transaction((rows: { title: string; ts: number }[]) => {
  for (const r of rows) insert.run(r.title, r.ts);
});
tx([
  { title: "first", ts: Date.now() },
  { title: "second", ts: Date.now() },
]);

const recent = db
  .query("SELECT id, title FROM posts ORDER BY id DESC LIMIT 10")
  .all() as { id: number; title: string }[];

console.log(recent);

prepared statement, transaction, named parameter — 모두 동기 API. Bun과 SQLite가 한 프로세스 안에 같이 있어서 FFI 비용이 없다.

어디서 쓰나

  • 로컬 데이터 캐시 — CLI 도구의 메타데이터 저장, 작은 서비스의 임시 저장소.
  • embedded analytics — 작은 보드 디스플레이, 미니 BI.
  • 테스트 DB — Postgres 대신 in-memory SQLite로 통합 테스트.
  • 에이전트의 메모리 — AI 에이전트가 자기 컨텍스트/메모리를 SQLite에 저장하는 패턴이 흔하다.

bun:sqlite는 Bun을 "다음 세대 scripting 환경"으로 만든다. Python의 sqlite3 표준 라이브러리 자리를 JS 생태계에 가져왔다.


7장 · Bun shell ($) — 약속 이상이었던 또 하나

약속

zx(Google의 JS 셸 도구)에 영감을 받은, 타입 안전한 셸 명령어. 단, zx는 Node 위에 서드파티로 도는 반면 Bun shell은 런타임에 내장.

코드 — pipeline 예시

// shell.ts
import { $ } from "bun";

const files = await $`ls -1 src`.text();
console.log(files);

// 파이프와 redirect
const wc = await $`cat package.json | wc -l`.text();
console.log("lines in package.json:", wc.trim());

// 안전한 변수 보간 (shell injection 안전)
const target = "components";
await $`mkdir -p dist/${target}`;

// 실패 시 throw
try {
  await $`exit 1`;
} catch (e) {
  console.log("caught exit code:", e.exitCode);
}

// 환경 변수
const home = (await $`echo $HOME`.text()).trim();
console.log("home:", home);

핵심은 두 가지다.

  1. shell injection 안전성${target} 같은 JS interpolation은 자동 escape된다. os.system(...) 류의 잘못된 패턴이 처음부터 막힌다.
  2. TypeScript 통합 — IDE가 자동 완성과 타입 정보를 제공.

현실 — 누가 쓰나

Bun shell은 빌드 스크립트, dev tool의 사실상 표준 자리로 빠르게 올라왔다. 옛날에 package.json"scripts"에 길게 적던 명령을 scripts/build.ts로 옮기고, Bun shell로 짠다.

  • make의 자리를 일부 가져갔다.
  • shell script의 가독성 문제(quoting hell, special character escape)를 해결.
  • cross-platform — Bun shell은 Windows에서도 같은 코드로 동작 (POSIX 흉내).

Bun shell은 Bun을 안 쓰는 사람도 Bun을 깔게 만드는 기능이다. shell script가 길고 fragile해질 때, bun + $이 나타난다.


8장 · bunfig.toml, executable bundles, 그 외

bunfig.toml

Bun의 설정 파일. registry, install behavior, test runner 옵션, JSX 변환 옵션 등을 한 파일에.

# bunfig.toml
[install]
registry = "https://registry.npmjs.org/"
production = false

[install.cache]
dir = "~/.bun/install/cache"

[test]
preload = ["./test/setup.ts"]

[run]
silent = false

[loader]
".ts" = "tsx"

package.json의 일부 영역(scripts, deps)을 침범하지 않고, Bun 특화 설정만 모은다. 좋은 분리.

Executable bundles — bun build --compile

bun build --compile --target=bun-linux-x64 ./cli.ts --outfile mycli

JavaScript 코드를 단일 실행 파일로 패키징. Bun 런타임이 그 안에 같이 들어간다. 결과 파일 ~70MB 정도, 그러나 의존성 없이 실행 가능.

비교 대상:

  • Node 22: node --experimental-sea-config (Single Executable Application) — 비슷한 기능, 더 manual.
  • Deno 2: deno compile — 안정적인 동등 기능.
  • pkg (vercel): legacy 도구, 더 이상 활발하지 않음.

Bun의 --compile빠른 시작(Bun의 cold start advantage가 적용)과 무의존 배포를 한 번에 준다. CLI 도구 만드는 사람들이 자주 쓴다.

그 외

  • Bun watch (bun --watch) — Node의 --watch와 동등, 더 빠른 재시작.
  • Bun macros — 빌드 시점에 JS를 실행해서 결과를 인라인. 정적 데이터 패치.
  • Bun lockfile auditing — 보안 advisory 통합.

9장 · Node 22+의 반격 — Bun이 던진 공을 Node가 받았다

Bun의 가장 큰 기여 중 하나는 Node의 진화를 가속화한 것이다. 2023년 1.0 시점에 Node가 "느리고 옛날 짜고 무거운" 이미지였다면, 2026년의 Node는 진짜로 빠르고 modern해졌다. 일부는 Bun이 노린 약점을 직접 막은 것이다.

Bun이 자랑한 것Node의 답
무설정 TSnode --experimental-strip-types (Node 22) → Node 24에서 stable. .ts 파일을 직접 실행.
내장 test runnernode:test 모듈 + node --test. 2024년 이후 stable, mock 지원도 들어옴.
native fetchNode 18부터 stable. 더 이상 node-fetch 안 깔아도 됨.
watch modenode --watch. nodemon 안 써도 됨.
환경변수 파일node --env-file .env. dotenv 안 써도 됨.
Single executable appnode --experimental-sea-config.
코어 모듈 importnode:fs, node:path prefix.

Bun의 가장 큰 영향은 "Bun을 깔게 만든 것"이 아니라, "Node를 자극해서 Node가 좋아지게 만든 것"이다. 모든 JS 개발자가 그 혜택을 본다, Bun을 쓰지 않더라도.

Node 22+는 cold start가 여전히 Bun보다 느리고, install 속도는 npm이라 한참 느리지만, dev 경험의 격차는 극적으로 좁혀졌다.


10장 · Deno 2와의 비교 — Node 호환 모드를 푼 또 다른 옵션

Deno도 2024년 Deno 2를 출시하며 큰 방향 전환을 했다. Node 호환 모드를 적극적으로 켜고, package.json을 native하게 지원하고, npm 모듈을 import 가능하게 만들었다.

2026년 시점에서 Bun vs Deno 2를 단순화하면:

BunDeno 2
런타임 속도가장 빠름Bun보다 약간 느리지만 충분
Node 호환광범위, 일부 영역 약함Deno 2에서 크게 개선, 안정적
권한 모델Node와 같은 "모든 권한"기본 deny — 명시적 permission flag
표준 라이브러리비교적 작음풍부, 잘 정리됨 (@std)
TS 지원무설정, 빠름무설정, type check도 함
보안 stance빠름 우선안전 우선 (sandboxing)
패키지 매니저bun installdeno install (jsr.io 또는 npm:)

Deno 2는 security 우선의 stance, Bun은 속도 우선의 stance. 그러나 실제 사용 시 둘 다 점점 비슷해지고 있다 — Node 호환은 양쪽 다 가능. Deno의 --allow-net, --allow-read 같은 권한 flag는 production 보안에서 차별점이 있지만, dev 경험에서는 마찰을 추가한다.

사용자의 운영 환경이 고보안 prod (예: 정부 시스템, 금융) 이라면 Deno 2가 유리. 개발 도구/CI/일반 웹 서비스라면 Bun이 dev 경험에서 우세. 회사 전체 표준 런타임이라면 여전히 Node가 default.


11장 · Broken promises — 약속을 지키지 못한 영역

이 글의 핵심 절. 어디서 Bun이 약속을 지키지 못했나.

11.1 "Node를 drop-in 대체"라는 narrative

광고 문구는 그렇게 시작했다. 그러나 실제로는 legacy enterprise 코드베이스에서 native addon, APM agent, custom debugger, complex cluster 사용, 등으로 Bun이 못 도는 경우가 종종 있다. "drop-in"이 가능한 건 작은 코드베이스다.

11.2 Windows 지원

1.0 시점에 Windows 지원은 가장 큰 broken promise였다. 처음 1년은 Linux/macOS만 본격 지원했고, Windows는 WSL을 권장했다. 2024년 후반부터 native Windows 지원이 추가됐지만, 2026년 mid 시점에도 일부 모듈은 Windows에서 edge case가 있다.

11.3 안정성 (1.0 직후의 시기)

1.0 GA 직후 6개월은 production에서 "이상한 메모리 누수", "OOM kill", "race condition" 등의 보고가 적지 않았다. 회사 단위로 운영하던 곳들이 Node로 되돌아간 사례가 있다. 2025년 들어 안정성은 크게 좋아졌지만, 첫 GA의 충격은 평판에 영구적인 흔적을 남겼다.

11.4 ecosystem maturity (관측성·monitoring)

Bun의 약한 곳: APM agent의 instrumentation. New Relic·Datadog·Dynatrace 등 메이저 APM이 Node를 위해 쌓아온 deep instrumentation을 Bun에서 똑같이 받기까지 2년 이상 걸렸다. 이건 운영 팀이 Bun을 채택하지 못하게 만드는 큰 이유다 — "보이지 않는 production은 production이 아니다".

11.5 가끔 깨지는 Node API

fs.readFile의 callback 시그니처, process.binding의 내부 의존, worker_threads의 일부 edge case — 점점 좁아지지만 0이 되지 않은 차이들. 큰 라이브러리(예: TypeScript 컴파일러 일부 path)가 Bun에서만 살짝 다르게 도는 경우가 있다.


12장 · Over-delivered — 약속 이상이었던 영역

균형을 위해, Bun이 기대 이상으로 잘한 것들도 정리한다.

12.1 Bun shell

위에서 다뤘다. 9장에 적은 대로, Bun shell은 본인 약속을 넘어 산업 default가 된 도구다.

12.2 bun install 속도와 안정성

처음 광고된 25x는 측정 시나리오에 따라 달랐지만, 일관되게 빠른 install은 약속을 잘 지켰다. cold cache, hot cache, lockfile diff 같은 작업이 다 합리적인 속도.

12.3 bun:sqlite

6장에서 다뤘다. 약속에 없던 기능이지만 만들어서 좋은 결과.

12.4 cold start

Lambda·Cloudflare Workers 같은 serverless 환경에서 Bun의 cold start advantage가 운영적으로 의미 있는 차이를 만들었다. 처음 광고 때보다 더 큰 use case가 됐다.

12.5 Bun 팀의 응답성

issue가 보고되면 빠르게 반응. lockfile 형식 전환, Windows 지원, Node API 추가 — 커뮤니티 피드백을 진지하게 반영했다. 이건 평가받을 만하다.


13장 · 누가 production에서 Bun을 쓰나 — 2026 mid 스냅숏

운영에서 Bun을 본격 채택한 회사들의 패턴.

케이스 A — 작은 API 게이트웨이 / Edge function

  • fly.io 위의 작은 Bun.serve 인스턴스로 라우팅·인증·rate limit만 처리.
  • Cloudflare Workers는 Bun이 아니라 자체 V8 isolate를 쓰지만, 로컬 dev에서 Wrangler 대신 Bun.serve로 mock 서버를 띄우는 패턴이 흔하다.

케이스 B — CLI 도구

  • Bun으로 CLI를 만들고, bun build --compile로 단일 실행 파일로 배포. 사용자는 Bun을 깔지 않아도 됨.
  • Prisma 같은 일부 CLI가 Bun으로 일부 컴포넌트를 작성.

케이스 C — 내부 도구·dev tool

  • 회사 내 dashboard, deploy 자동화, CI helper. Bun.serve + bun:sqlite로 만든 작은 운영 도구가 매우 흔하다.

케이스 D — AI 에이전트 런타임

  • AI 에이전트의 sandbox 런타임으로 Bun을 쓰는 패턴이 증가. 빠른 cold start + bun:sqlite로 메모리 + Bun shell로 도구 실행 조합. e2b·Modal 등 일부 AI infra에서 Bun runner 옵션을 제공.

케이스 E — 매우 적게 — 대규모 모놀리스

  • 거의 없다. 대규모 trad. 모놀리스가 Bun으로 갈아탔다는 공개 사례는 손에 꼽는다. 위험 대비 보상이 매력적이지 않고, APM·debugger 생태계의 압박이 크다.

명시적으로 언급된 사례 (공개 글 기준)

  • Vercel: 자사 일부 빌드 도구, edge function의 일부 워크플로우에서 Bun을 채택.
  • Mintlify: 문서 빌더 일부에서 Bun.
  • Codeium / Replit / Glitch: 사용자 실행 환경의 일부 옵션으로 Bun.
  • 다수의 startup: 초기 API의 단일 인스턴스로 Bun.serve+Hono.

14장 · 의사결정 — 어디서 Bun이 맞고, 어디서 Node가 맞나

Honest take.

Bun이 맞는 곳

  1. CI 속도가 중요한 dev 작업bun install + bun test만으로도 의미 있는 win.
  2. CLI 도구bun build --compile로 단일 파일 배포, 빠른 시작.
  3. Edge/serverless — cold start 이득.
  4. scripting / dev tooling — Bun shell.
  5. SQLite 헤비한 작은 서비스bun:sqlite의 성능.
  6. 새 프로젝트, 외부 의존성 적음 — 호환 이슈 만나기 어려움.

Node가 여전히 맞는 곳

  1. 대규모 enterprise 모놀리스 — APM, custom debugger, native addon ecosystem 압도적.
  2. legacy 코드베이스의 운영 안정성 — 변경 위험이 보상보다 큼.
  3. deep observability가 필수인 production — Datadog/NR auto-instrumentation의 완성도.
  4. regulatory가 까다로운 환경 — Node가 audit·CVE 채널이 잘 정비됨.
  5. 회사 표준 런타임 — 사람을 뽑고 가르치기 쉬움.

Deno 2가 맞는 곳

  1. 고보안 환경 — permission model이 운영적으로 의미 있는 곳.
  2. 표준 라이브러리 중심의 개발@std의 안정성.
  3. 새 프로젝트지만 Node 호환은 원함 — Deno 2의 npm compat이 잘 됨.

세 런타임은 더 이상 zero-sum이 아니다. 한 회사 안에서 Bun (dev tooling/CI/CLI), Node (메인 서비스), Deno (고보안 부분)을 섞어 쓰는 경우가 늘고 있다. "어느 하나가 다 이긴다"는 narrative는 2026년에 더 이상 통하지 않는다.


에필로그 — 약속, 청구서, 그리고 다음 라운드

Bun 1.0 GA에서 2년 반. 정리하자면:

Bun은 "Node를 한 번에 갈아내는" 약속을 지키지 못했다. 그러나 "JS 생태계를 빠르게 만드는" 약속은 (Bun 안에서, 그리고 Node에 자극을 줘서) 지켰다. 그 결과 모든 JS 개발자가 더 좋은 도구를 쓰게 됐다 — Bun을 채택하든 안 하든.

기억할 점:

  • Bun = 런타임이 아니라 toolkit으로 생각하는 사람이 만족도가 높다. bun install, bun test, Bun shell, bun build --compile을 각각 골라 쓰고, 런타임 채택은 천천히.
  • Node 22+는 진짜 좋아졌다. "옛날 Node"의 인상을 가지고 있다면 한 번 다시 보자.
  • Deno 2는 잊지 마라. 보안/표준 중심의 운영에 적합한 옵션.
  • APM 호환은 여전히 한 발 늦다. production 갈 거면 미리 확인.
  • Windows는 점점 좋아지지만 1급 시민이 된 지는 얼마 안 됐다.

채택 체크리스트 (Bun을 도입하기 전)

  • 우리 APM (NR/DD/Sentry/...)이 Bun을 공식 지원하나? instrumentation은 어느 수준인가?
  • 우리가 의존하는 native addon (sharp, node-canvas, ...)이 Bun에서 도나?
  • 우리 CI가 Bun을 빌드 단계에서 받을 수 있나? Docker base image는?
  • 우리 사람들이 Bun 디버깅에 적응할 시간이 있나? (Chrome DevTools 프로토콜 호환의 한계 이해)
  • 어디서 Bun을 도입할 건가? (단계: lockfile만 → install만 → test/scripts → CLI → 작은 서비스 → 메인 서비스)
  • rollback 시나리오는? Bun→Node 되돌리기 비용은?

흔한 anti-pattern

  • "Node 코드를 그대로 Bun으로 배포"하고 비기능 요구사항(관측성)을 잊는다.
  • CI 속도 개선이 매력적이라 production 런타임까지 한 번에 바꾼다. 둘은 분리할 수 있고, 분리해야 안전하다.
  • Bun shell을 너무 무겁게 쓴다 — 한 줄 명령은 그냥 child_process.spawnSync도 OK.
  • bun:sqlite를 production primary DB로 쓴다. SQLite는 어디까지나 단일 노드 데이터 — Postgres 자리에 두지 마라.
  • 벤치마크 차트를 그대로 믿는다. 본인 워크로드에서 측정해보자. "1k qps에서 빠른 게 100k qps에서도 빠른가"는 다른 질문이다.

다음 글 예고

  • "Hono 가이드 2026 — runtime-agnostic 웹 프레임워크 (Bun·Deno·Workers·Vercel·Node)" — 한 코드베이스로 다섯 런타임에서 도는 패턴.
  • "Node 22~24의 진화 — strip-types, node:test, --watch, --env-file이 바꾼 dev experience" — Bun을 안 써도 받는 혜택들.
  • "Deno 2 retrospective" — Deno 2의 Node 호환 베팅이 1년 뒤 어떻게 됐는지.

참고 / References

Bun 공식 자료

Node.js

  • Node.js 공식 — https://nodejs.org/
  • node:test 문서 — Node 22+ 기준의 native test runner.
  • Node strip-types 제안 (TC39 type annotations) — TypeScript erasable syntax와의 관계.

Deno

  • Deno 공식 — https://deno.com/
  • Deno 2 announcement — Node 호환 베팅과 npm: 지원.

벤치마크와 비교 글

  • "Bun vs Node vs Deno: HTTP performance" — 여러 커뮤니티 벤치마크 (워크로드별 결과 다름, 본인 워크로드 측정 권장).
  • TechEmpower Web Framework Benchmarks — Bun·Node·Deno 위 프레임워크 비교의 표준 출처.

웹 프레임워크 / 런타임-agnostic

  • Hono — https://hono.dev/ — Bun·Deno·Workers·Node 모두 지원.
  • itty-router, h3 — 같은 카테고리의 다른 옵션.

SQLite 관련

  • bun:sqlite 문서 — Bun 내장 SQLite의 API.
  • better-sqlite3 — Node 쪽의 사실상 표준 비교 대상.

Bun shell, zx

운영 채택 사례 (공개된 글 기준)

  • Vercel 엔지니어링 블로그의 Bun 도입 글.
  • 다수의 startup 회고록 — "왜 Bun을 (안) 골랐는가" 류의 글.

비판적 시각

  • Bun의 early-days production 이슈 회고.
  • APM auto-instrumentation의 한계에 대한 운영팀의 글.
  • Node가 따라잡으면서 차이가 좁아진 영역에 대한 분석.

Bun Retrospective 2026 — 1.0 GA Promises vs Reality: Runtime, Bundler, Package Manager, Test Runner, Shell (An Honest Take from the Bun 2 Era)

Prologue — The 2023 promise, the 2026 invoice

I remember the air around September 2023, when Bun 1.0 shipped GA. The demos were striking. bun install was 25x faster than npm install, bun test was 13x faster than Jest, bun run started 4x faster than Node. Those charts were on every other Twitter timeline. Half of the room said "Node is over." The other half said "this is just another Yarn 1.0 moment."

It is May 2026. Time to read the invoice.

Bun kept about half of its promises. But it was not the half we expected.

bun install really is fast — promise kept. bun test is fast. Bun.serve really is lightweight and quick. But the scenario of "Bun replaces Node in production" is rare. The honest picture as of mid-2026 looks like this:

  • Bun-as-a-toolkit is widely adopted — bun install, bun test, Bun shell. CI times cut in half.
  • Bun-as-a-runtime remains niche — small API gateways with Bun.serve, edge workers, internal tools. Stories of a large monolith migrating to Bun are scarce.
  • Node 22+ played a surprisingly good defensenode --experimental-strip-types, node:test, node --watch, native fetch. Node returned Bun's serve from its own side of the court.

This post is an honest retrospective of the two and a half years after 1.0 GA. What over-delivered, what was a broken promise, and where Bun is the right call versus where Node still is.


1. The Bun runtime — Zig + JavaScriptCore, the original bet

The promise

Bun made three bets from day one.

  1. Rewrite the runtime in Zig — unlike Node which sits on V8 plus Node's own C++ glue, Bun is written in Zig to keep startup time and memory footprint small.
  2. JavaScriptCore (JSC), not V8 — the engine Safari uses. The general view is that JSC is lighter on startup and memory than V8.
  3. Node API compatibilityfs, http, path, crypto, and friends are supported as-is. The promise being "drop a Node project into Bun and it runs."

The combination was meant to be a drop-in replacement that simply ran Node code, faster.

Reality — runtime speed

Benchmarks consistently show Bun beating Node. But where it beats Node matters.

Cold start ("console.log"):
  Node 22:  35~60ms
  Bun 1.2:  10~20ms
  Deno 2:   25~40ms

HTTP throughput (simple echo, single core):
  Node 22 (http):        80k req/s
  Node 22 (uWebSockets) 280k req/s
  Bun.serve:             250~310k req/s
  Hono on Bun:           220~280k req/s
  Deno serve:            200~240k req/s

SQLite query (1M rows, simple select):
  better-sqlite3 (Node): 4.2s
  bun:sqlite:            1.6s

Fast, yes. But look at the shape of the gap.

  • Cold start, 2~3x faster: meaningful in AWS Lambda or other cold-start-sensitive workloads. Irrelevant in a long-running server.
  • HTTP throughput: when Node is not on its default http module — using uWebSockets or fastify — the gap narrows substantially. A fair comparison is "optimized vs optimized," not "default vs default."
  • SQLite: a genuine win. bun:sqlite is FFI directly into SQLite, so the N+1 chatter drops out.

The point: Bun is fast not because "JS executes faster," but because "I/O and native interfaces are lighter." For CPU-bound JS code, the gap against Node is often small.

Reality — Node compatibility

Bun advertised "90%+ Node compatibility" at 1.0. Mid-2026 reality, by area:

AreaCompat statusUsable?
fs, path, os, utilNear completeYes
http, https, urlNear completeYes
cryptoMostly OK, some legacy API gapsYes
child_processMostly OKYes
worker_threadsOKYes
clusterPartial, edge cases differCaution
Native addons (N-API)Broad support, a handful missingYes (mostly)
vm moduleA few options missingCaution
inspector (debugger protocol)PartialCaution
async_hooksPartial — affects APM toolsCaution
process.binding internalsNot supported (intentional)No

Small services rarely hit a wall. But APM (Application Performance Monitoring) agents, OpenTelemetry auto-instrumentation, and legacy native addons still break. New Relic, Datadog and similar landed official Bun support only by late 2025, and some instrumentation remains partial.

"Node code drops in and runs" is true in small codebases and half-true in big ones. What breaks is rarely business code — it is the surrounding infrastructure (tracing, profiler, debugger).


2. bun install — the clearest win

Of all the areas, the least debatable win is bun install.

Benchmark

Hot cache, mid-size repo (~800 deps):
  npm install:    18s
  yarn install:    9s
  pnpm install:    5s
  bun install:     1.6s

Cold cache, same repo:
  npm install:    65s
  yarn install:   35s
  pnpm install:   18s
  bun install:    7s

bun install brought Rust-Cargo-like or Go-module-resolver-like speed into the JS world. This is a real DX differencebun install finishes in five to ten seconds on CI every time, and locally a lockfile change does not test your patience.

A short history of the lockfile war

When 1.0 first shipped, bun.lockb was a binary lockfile. The goal was fast parsing, but PR reviewers could not see a diff. Through 2024 and 2025 the community pushed back hard, and Bun eventually introduced bun.lock (a text, JSON-superset format). As of 2026, new projects default to the text lockfile. Some older projects still keep bun.lockb.

Lesson: A DX bet that optimizes one side (speed) at the cost of another (diffability, code review) will be sent back by the community. Bun acknowledged this and corrected quickly. That deserves credit.

Where it is actually used

bun install can be adopted without switching the runtime. CI pipelines using bun install followed by npm run build or node server.js are common.

  • Vercel and Netlify offer bun install as a build option.
  • Next.js, SvelteKit, Astro and other major frameworks build cleanly when deps are installed with Bun.
  • Docker images combining the oven/bun base for builds with a Node runtime cut build time roughly in half.

bun install is the safest first step into Bun adoption — and by far the most common one.


3. bun test — fast, but adoption is mixed

The promise

bun test claims Jest-compatible (or close-to-Jest) API, runs much faster than Jest, and executes TypeScript directly without configuring a transform.

Reality

Benchmarks show Bun test 310x faster than Jest. Against Vitest, the gap is 13x in either direction depending on the suite. Adoption is mixed.

ToolMid-2026 adoption (impression)Strength
JestStill #1 (legacy weight)Ecosystem, plugins, stability
VitestRapidly passing into #2Vite integration, ESM-native, watch UX
Bun test#3, common in new projectsSpeed, zero-config TS
Node test (node:test)Slowly growingNo deps, official

Bun test has not displaced the main stack for a few reasons.

  1. Jest's plugin / ecosystem: snapshot serializers, custom matchers, jest-axe, jest-extended, and many more. Porting all of them takes time.
  2. Mocking differences: jest.mock() compatibility keeps improving, but edge cases differ.
  3. Monorepo integration: Turborepo and Nx integrate more smoothly with Jest and Vitest.

Bun test is most common in new single-package projects. In large monorepos, Vitest still tends to win.


4. The Bun bundler — aimed at esbuild's seat, but

The promise

Bun ships a built-in bundler — bun build. It openly cites esbuild as inspiration and claims to be faster.

Reality — the 2026 bundler landscape

The JS bundler landscape became much more crowded than it was in 2023.

BundlerPositionNotes
esbuildStill dominantSmallest surface, most stable
Vite (Rollup)The dev-server standardHMR, plugin ecosystem
RolldownThe Vite next-genRust-based, can beat esbuild
TurbopackNext.js defaultVercel's bet, Webpack successor
Bun bundlerNiche, mainly inside the Bun ecosystemFast, zero-config TS, integrates with Bun runtime
swcpackswc's bundler branchAn earlier Vercel-side experiment

The Bun bundler is fast, that is true. But the plugin ecosystem is far smaller than esbuild's or Rollup's. For PostCSS, SVGR, MDX and similar areas, esbuild plugins do not always work as-is (Bun does try to be esbuild-plugin-compatible).

The Bun bundler is a sensible default inside the Bun ecosystem. Small SPAs served by Bun.serve, the dist of a Bun-only CLI tool. It did not become an industry-wide replacement for esbuild. That seat is being chased by Rolldown.


5. Bun.serve() — fighting for the web-framework seat

The promise

Bun.serve is "build a fast HTTP server without express." Fastify-level throughput from the standard library.

A 30-second look

// server.ts
import { serve } from "bun";

serve({
  port: 3000,
  async fetch(req) {
    const url = new URL(req.url);
    if (url.pathname === "/hello") {
      return new Response("hi");
    }
    if (url.pathname === "/echo" && req.method === "POST") {
      const body = await req.json();
      return Response.json({ ok: true, body });
    }
    return new Response("not found", { status: 404 });
  },
});

console.log("listening on http://localhost:3000");

The web-standard Request/Response API matters. Same paradigm as Cloudflare Workers and Deno — Bun lined itself up there.

Reality — the rise of Hono

Bun.serve is quick. But you still need a router, middleware, and validation on top of it before you have a real app. The de-facto standard in 2026 is Hono.

// hono on bun
import { Hono } from "hono";

const app = new Hono();
app.get("/hello", (c) => c.text("hi"));
app.post("/users", async (c) => {
  const body = await c.req.json();
  return c.json({ id: 1, ...body });
});

export default { fetch: app.fetch, port: 3000 };

Hono runs the same code on Bun, Deno, Cloudflare Workers, Vercel Edge, and Node. That means picking Hono lets you defer or change the runtime choice. That is a big selling point. Hono-on-Bun is operationally safer than code tightly coupled to Bun.serve.

Bun.serve is good. But when people actually build, they pick Hono more often. Bun does not fight this — the Bun docs themselves point at Hono as the recommended router on top of Bun.serve.


6. bun:sqlite — the biggest over-delivery

If forced to pick the single feature that exceeded its billing, bun:sqlite is the answer.

Why it is good

On Node, using SQLite means installing better-sqlite3 (native addon) or sqlite3 (async). Both build native code at install time and occasionally hit cross-platform issues. bun:sqlite ships inside the runtime — no build step, just import and use.

Code

// sqlite.ts
import { Database } from "bun:sqlite";

const db = new Database("app.db");
db.run(`
  CREATE TABLE IF NOT EXISTS posts (
    id INTEGER PRIMARY KEY,
    title TEXT NOT NULL,
    created_at INTEGER NOT NULL
  )
`);

const insert = db.prepare(
  "INSERT INTO posts (title, created_at) VALUES (?, ?)"
);
const tx = db.transaction((rows: { title: string; ts: number }[]) => {
  for (const r of rows) insert.run(r.title, r.ts);
});
tx([
  { title: "first", ts: Date.now() },
  { title: "second", ts: Date.now() },
]);

const recent = db
  .query("SELECT id, title FROM posts ORDER BY id DESC LIMIT 10")
  .all() as { id: number; title: string }[];

console.log(recent);

Prepared statements, transactions, named parameters — all synchronous. Bun and SQLite share a process, so there is no FFI overhead.

Where it is used

  • Local data caches — metadata stores for CLI tools, scratch storage for tiny services.
  • Embedded analytics — small kiosk dashboards, mini BI.
  • Test databases — integration tests against in-memory SQLite instead of Postgres.
  • Agent memory — AI agents frequently store their context/memory in SQLite.

bun:sqlite makes Bun into a "next-generation scripting environment." It brought the role of Python's sqlite3 standard library into the JS ecosystem.


7. Bun shell ($) — another above-promise feature

The promise

A type-safe shell, inspired by zx (Google's JS shell tool). The difference is that zx runs on top of Node as a third-party tool, while Bun shell is built into the runtime.

Code — a pipeline

// shell.ts
import { $ } from "bun";

const files = await $`ls -1 src`.text();
console.log(files);

// Pipes and redirects
const wc = await $`cat package.json | wc -l`.text();
console.log("lines in package.json:", wc.trim());

// Safe interpolation (shell-injection-safe)
const target = "components";
await $`mkdir -p dist/${target}`;

// Throws on non-zero exit
try {
  await $`exit 1`;
} catch (e) {
  console.log("caught exit code:", e.exitCode);
}

// Environment variables
const home = (await $`echo $HOME`.text()).trim();
console.log("home:", home);

Two things matter most.

  1. Shell-injection safety — JS interpolation like ${target} is automatically escaped. The os.system(...) family of footguns is blocked at the type level.
  2. TypeScript integration — your IDE gives autocomplete and type info.

Reality — who uses it

Bun shell climbed quickly to the de-facto standard for build scripts and dev tools. The long incantations that used to live in package.json "scripts" move into scripts/build.ts and are written with Bun shell.

  • It took some of make's seat.
  • It solved the readability problems of shell scripts (quoting hell, special-character escaping).
  • It is cross-platform — Bun shell behaves the same on Windows (POSIX emulation).

Bun shell is the feature that makes people install Bun even if they do not run Bun. When a shell script grows long and fragile, bun + $ shows up.


8. bunfig.toml, executable bundles, and the rest

bunfig.toml

Bun's configuration file. Registry, install behavior, test runner options, JSX transform options — all in one file.

# bunfig.toml
[install]
registry = "https://registry.npmjs.org/"
production = false

[install.cache]
dir = "~/.bun/install/cache"

[test]
preload = ["./test/setup.ts"]

[run]
silent = false

[loader]
".ts" = "tsx"

It does not invade the package.json (scripts, deps) and groups only Bun-specific settings. Clean separation.

Executable bundles — bun build --compile

bun build --compile --target=bun-linux-x64 ./cli.ts --outfile mycli

Package your JavaScript into a single executable. The Bun runtime is packaged in alongside it. The output file lands around 70MB, but it runs without any external dependency.

Compare with:

  • Node 22: node --experimental-sea-config (Single Executable Application) — similar idea, more manual.
  • Deno 2: deno compile — stable equivalent.
  • pkg (Vercel): legacy tool, no longer actively maintained.

bun --compile gives you Bun's fast startup plus no-dependency distribution in one move. CLI authors use it often.

The rest

  • Bun watch (bun --watch) — equivalent to Node's --watch, with a faster restart.
  • Bun macros — run JS at build time and inline the result. Useful for static data baking.
  • Bun lockfile auditing — integrated security advisories.

9. The Node 22+ counter-punch — Node fielded the ball Bun served

One of Bun's largest contributions was accelerating Node's evolution. If at 1.0-time Node felt "old, slow, heavy," 2026 Node is genuinely fast and modern. Some of that improvement plugged the very gaps Bun targeted.

What Bun showed offNode's answer
Zero-config TSnode --experimental-strip-types (Node 22) → stable in Node 24. Run .ts files directly.
Built-in test runnernode:test module + node --test. Stable since 2024, mocks included.
Native fetchStable since Node 18. No more node-fetch.
Watch modenode --watch. No more nodemon.
Env-file flagnode --env-file .env. No more dotenv.
Single executable appnode --experimental-sea-config.
Core module importsnode:fs, node:path prefix.

Bun's biggest impact was not "people installing Bun," but "pressuring Node into getting better." Every JS developer benefits, Bun-user or not.

Node 22+ still has slower cold start than Bun and still ships npm for install (much slower), but the dev-experience gap shrank dramatically.


10. Deno 2 — the security-first alternative that loosened Node-compat

Deno also pivoted with Deno 2 in 2024. Node compatibility mode was turned on aggressively, package.json is supported natively, and npm modules can be imported.

A simplified mid-2026 view of Bun vs Deno 2:

AxisBunDeno 2
Runtime speedFastestA touch behind Bun, plenty fast
Node compatBroad, some gapsBig jump in Deno 2, stable
Permission modelNone by default (like Node)Deny-by-default, explicit permission flags
Standard libraryRelatively smallRich and curated (@std)
TS supportZero-config, fastZero-config, with type-check
Security stanceSpeed firstSafety first (sandboxing)
Package managerbun installdeno install (jsr.io or npm:)

Deno 2 sits in the security-first corner; Bun sits in the speed-first corner. In practice they have been converging — Node compat is broad on both sides. Deno's --allow-net, --allow-read and similar flags give a real edge in production security but add friction in dev.

If your operating environment is high-security prod (government, finance), Deno 2 has an edge. For dev tooling, CI, and general web services, Bun wins on developer experience. For a company-wide standard runtime, Node remains the default.


11. Broken promises — where Bun fell short

The core of an honest retrospective: where did Bun not deliver.

11.1 The "drop-in Node replacement" narrative

That was the launch line. In practice, legacy enterprise codebases with native addons, APM agents, custom debuggers, or complex cluster usage often still do not work cleanly. "Drop-in" is true in small codebases.

11.2 Windows support

At 1.0 this was the biggest broken promise. For the first year, only Linux/macOS had first-class support, and the official advice for Windows was WSL. Native Windows support landed in late 2024, but even in mid-2026, some modules still hit edge cases on Windows.

11.3 Stability (the months right after 1.0)

In the six months after 1.0 GA, the field saw a non-trivial number of reports — "weird memory leaks," "OOM kills," "race conditions." Some early adopters running Bun in production rolled back to Node. Stability has improved a lot through 2025, but the shock of those first months left a lasting mark on reputation.

11.4 Observability and the APM gap

A persistent weak spot: APM instrumentation. The deep instrumentation New Relic, Datadog, and Dynatrace built over years on Node took 2+ years to reach parity for Bun. This alone keeps operations teams away — "production you cannot see is not production."

11.5 The occasional Node API rough edge

The exact callback signature of fs.readFile, internal use of process.binding, edge cases in worker_threads — narrower than before but not zero. Large libraries (including some paths in the TypeScript compiler) occasionally behave slightly differently on Bun.


12. Over-delivered — where Bun exceeded expectations

For balance, the things Bun did better than promised.

12.1 Bun shell

Covered above. As in section 7, Bun shell exceeded its mandate and became an industry default tool.

12.2 bun install speed and stability

The original 25x number varied by scenario, but consistently fast installs kept the promise. Cold cache, hot cache, lockfile diffs — all reasonable.

12.3 bun:sqlite

Section 6. A feature not in the original headline that turned into a hit.

12.4 Cold start

Cold-start advantage on Lambda and Cloudflare Workers became a bigger operational use case than the original framing.

12.5 The Bun team's responsiveness

When issues are reported they move quickly. Lockfile format pivot, Windows support, Node API additions — they took community feedback seriously. That deserves credit.


13. Who actually runs Bun in production — mid-2026 snapshot

The patterns I see in companies that have actually deployed Bun.

Case A — small API gateways / edge functions

  • Tiny Bun.serve instances on fly.io handling routing, auth, and rate limiting.
  • Cloudflare Workers uses its own V8 isolates, not Bun, but using Bun.serve locally as a mock server in place of Wrangler is a common dev pattern.

Case B — CLI tools

  • Build a CLI in Bun, then ship a single executable via bun build --compile. End users do not need Bun installed.
  • Some Prisma CLI components are written in Bun.

Case C — internal tools / dev tools

  • Company dashboards, deploy automation, CI helpers. Small ops tools made with Bun.serve plus bun:sqlite are very common.

Case D — AI agent runtimes

  • A growing pattern: running AI agents inside a Bun sandbox. Fast cold start + bun:sqlite for memory + Bun shell for tool execution is a strong combo. Some AI infra providers (e2b, Modal) offer a Bun runner option.

Case E — almost never — large monoliths

  • Very rare. Public stories of a traditional large monolith migrating to Bun can be counted on one hand. The risk-to-reward is not attractive, and APM/debugger pressure is strong.

Named cases (per public posts)

  • Vercel: parts of internal build tooling and some edge-function workflows.
  • Mintlify: pieces of the docs builder.
  • Codeium / Replit / Glitch: Bun as an option in user runtime environments.
  • Many startups: early single-instance APIs on Bun.serve plus Hono.

14. Decisions — where Bun fits, where Node still fits

The honest take.

Where Bun fits

  1. Dev workflows where CI speed mattersbun install + bun test alone is a real win.
  2. CLI toolsbun build --compile for single-file ship, fast startup.
  3. Edge and serverless — cold-start advantage.
  4. Scripting / dev tooling — Bun shell.
  5. SQLite-heavy small servicesbun:sqlite performance.
  6. New projects with few external deps — compatibility issues unlikely.

Where Node still fits

  1. Large enterprise monoliths — APM, custom debuggers, native addon ecosystem.
  2. Legacy codebases where operational stability matters — change risk exceeds reward.
  3. Production where deep observability is mandatory — Datadog/NR auto-instrumentation maturity.
  4. Heavily regulated environments — Node's audit/CVE pipelines are well established.
  5. Company-standard runtime — easier to hire and train for.

Where Deno 2 fits

  1. High-security environments — the permission model is operationally meaningful.
  2. Development around a curated standard library@std stability.
  3. New projects that still need Node interop — Deno 2's npm compat is good now.

The three runtimes are no longer zero-sum. A growing number of companies run Bun (dev tooling/CI/CLI), Node (main service), and Deno (high-security pieces) side by side. The narrative of "one runtime wins all" does not hold in 2026.


Epilogue — promises, the invoice, and the next round

Two and a half years after Bun 1.0 GA. To summarize:

Bun did not keep its promise to "replace Node in one swoop." But it did keep its promise to "make the JS ecosystem faster" — both inside Bun and by pressuring Node from the outside. The result: every JS developer is using better tools today, whether they adopted Bun or not.

What to remember:

  • Think of Bun as a toolkit, not just a runtime. Pick bun install, bun test, Bun shell, and bun build --compile individually. Adopt the runtime later, slowly.
  • Node 22+ is genuinely better. If your image of Node is the old one, revisit it.
  • Do not forget Deno 2. It is the right option for security/standard-first operations.
  • APM compatibility still lags. Confirm support before going to production.
  • Windows keeps improving, but first-class status is recent.

Adoption checklist (before adopting Bun)

  • Does our APM (NR/DD/Sentry/...) officially support Bun? At what level of instrumentation?
  • Do our native addons (sharp, node-canvas, ...) run on Bun?
  • Can our CI accept Bun in the build stage? What about Docker base images?
  • Do our people have time to adapt to Bun debugging? (Understand the limits of Chrome DevTools protocol compatibility.)
  • Where exactly do we adopt Bun? (Phases: lockfile only → install only → test/scripts → CLI → small service → main service.)
  • What is the rollback story? Cost of moving back from Bun to Node?

Common anti-patterns

  • Deploying Node code on Bun and forgetting non-functional requirements (observability).
  • Letting CI speed lure you into changing the production runtime in one step. Those two changes can be separated, and should be.
  • Overusing Bun shell — for a single command, a plain child_process.spawnSync is fine.
  • Using bun:sqlite as the production primary database. SQLite is single-node data; do not put it in Postgres's seat.
  • Trusting the marketing benchmarks as-is. Measure on your own workload. "Fast at 1k qps" and "fast at 100k qps" are different questions.

Coming next

  • "Hono Guide 2026 — runtime-agnostic web framework (Bun/Deno/Workers/Vercel/Node)" — one codebase across five runtimes.
  • "Node 22~24 — how strip-types, node:test, --watch, and --env-file changed dev experience" — the benefits even non-Bun-users get.
  • "Deno 2 retrospective" — one year after the Node-compat bet, how did it play out.

References

Bun official

Node.js

  • Node.js homepage — https://nodejs.org/
  • node:test documentation — native test runner from Node 22+.
  • The Node strip-types proposal (TC39 type annotations) and its relationship to TypeScript erasable syntax.

Deno

  • Deno homepage — https://deno.com/
  • Deno 2 announcement — the Node-compat bet and npm: specifier support.

Benchmarks and comparisons

  • "Bun vs Node vs Deno: HTTP performance" — multiple community benchmarks (workload-dependent, measure yours).
  • TechEmpower Web Framework Benchmarks — the standard reference for Bun/Node/Deno framework comparisons.

Web frameworks / runtime-agnostic

  • Hono — https://hono.dev/ — runs on Bun, Deno, Workers, Node.
  • itty-router, h3 — other options in the same category.

SQLite

  • bun:sqlite documentation — the API for Bun's built-in SQLite.
  • better-sqlite3 — the de-facto Node comparator.

Bun shell, zx

Production adoption (per public writing)

  • Vercel engineering posts on Bun adoption.
  • Many startup retrospectives — "why we did (not) pick Bun."

Critical perspectives

  • Early-days production issue retrospectives on Bun.
  • Ops-team writing on the limits of APM auto-instrumentation.
  • Analyses of areas where Node has closed the gap.