Bun is a JavaScript runtime, package manager, test runner, and bundler in a single binary. Built on JavaScriptCore (Safari's engine) and written in Zig, it installs ~10x faster than npm, runs TypeScript natively, and replaces the node + npm + webpack + tsc + jest stack with one command. Three years old, production-credible since 1.0, and aggressively shipping.
Bun is the load-bearing tool collapse of the JavaScript ecosystem. The pitch isn't "we built a faster Node" — it's "we built a single tool that replaces node, npm/pnpm, tsc, webpack/esbuild, and jest, and each individual replacement happens to be the fastest in its category." That's a different value proposition than "shave 30ms off cold starts."
Under the hood: JavaScriptCore as the JS engine (Safari's engine, not V8), Zig as the implementation language (manual memory management, fast compiles, tight FFI), and a small army of native APIs that mirror Node's where they exist and add new ones (Bun.serve, Bun.write, Bun.file) where they don't.
The result that matters most in day-to-day work: bun install on a typical Next.js app finishes in 1-3 seconds vs 20-60 seconds for npm. bun test runs roughly 4x faster than Jest on the same suite. Cold start is ~5ms vs Node's ~30-50ms. None of these are individually transformative; together they change how the development loop feels.
Bun = bun (run) + bun install (npm) + bun test (jest) + bun build (webpack/esbuild) + bun x (npx) + bunx + bun init + native TypeScript + native JSX + native env-var loading + native SQLite + native HTTP server. One binary, one config file, one mental model. The "no JavaScript build chain" thesis, made executable.
If you can run a project with node script.ts (or tsx), you can probably run it with bun script.ts. The package.json stays unchanged, the node_modules structure is the same, the dependency resolution is npm-compatible. Bun then layers on the bits Node doesn't include: native TypeScript, the package manager, the test runner, the bundler.
The day-to-day commands:
# Install bun (macOS / Linux) curl -fsSL https://bun.sh/install | bash # In an existing Node project — drop in bun install # replaces npm install bun run "any-script-name" # replaces npm run script-name bun server.ts # run TypeScript directly, no transpile step # Test runner (Jest-API-compatible) bun test bun test --watch bun test --coverage # Bundler (esbuild-compatible API) bun build ./src/index.ts --outdir=./dist --target=node # Greenfield project — one command bun init # project + package.json + tsconfig + entrypoint
The hello-world server, in seven lines, no dependencies, no build step:
// server.ts — run with: bun server.ts Bun.serve({ port: 3000, fetch(req) { const url = new URL(req.url); if (url.pathname === "/") return new Response("hello world"); return new Response("not found", { status: 404 }); }, });
JavaScriptCore is Apple's JS engine, used in Safari. Compared to V8: faster cold start (lower startup overhead), better memory profile under sustained load, comparable peak throughput on optimised hot loops. JSC matters for cold-start-sensitive workloads (edge functions, CLI tools, serverless) where V8's optimisation budget hasn't paid off yet. For long-running servers, the difference is small — both engines are excellent.
Bun is genuinely production-ready, but it's three years old, not sixteen. There are real edges — mostly around long-tail npm packages, platform-specific behaviour, and the pace at which Bun itself ships breaking changes. Knowing where it bites means you can plan around it.
Packages that use node-gyp to compile C++ addons (e.g. better-sqlite3, some canvas bindings, ML/native libs) usually work but have edge cases. Bun's NAPI implementation is good; not 100%. Always check the project's "Bun compatibility" badge or test before committing to a stack choice.
Workers run on V8 isolates with Cloudflare's runtime; Bun isn't the runtime there. Use Bun for local dev, build, and tests; deploy to Workers using wrangler as normal. Don't try to ship Bun-specific APIs (Bun.serve, Bun.write) to Workers — they don't exist there. See tech/cloudflare/workers.
Bun ships fast. v1.0 to v1.x has had semantic-version compliance — but minor releases occasionally fix bugs by changing behaviour, and that has bitten teams pinned to specific versions. Pin your Bun version in .bunversion or CI; review release notes before bumping.
bun.lockb is a binary lockfile (faster to parse than text). Diffs in PRs are unreadable; some hosting platforms can't render it cleanly. Mitigations: bun install --print-text-lock for human-readable output; or convert to bun.lock JSON format (introduced 2024) for diff-friendly history.
Native Windows support shipped 2024 but lags macOS / Linux for some package compatibility and performance. If your team is mixed-OS, test on the actual OS the dev runs — don't assume parity. WSL2 is a reliable fallback.
process.env, fs, path, http, https, events, stream — all work. worker_threads works but with edge cases. vm module has gaps. child_process works but spawning is slightly different. The "drop-in for Node" frame is true for 95% of code; that 5% is where to test before betting the project on it.
Both runtimes are credible production choices in 2026. The interesting question is when each one is the better fit for a given project, not whether one is universally superior. The honest answer: Node is the safe default for production with mature dependency chains; Bun is the upgrade for greenfield projects, edge / cold-start workloads, and teams that value developer-loop ergonomics.
| Concern | Node.js | Bun |
|---|---|---|
| Cold start | ~30-50ms | ~3-10ms (4-10x faster) |
| Install speed | npm: 20-60s typical / pnpm: ~5-15s | bun install: 1-3s (~10x npm, ~3x pnpm) |
| Test runner | node:test (built-in but rough) / vitest / jest | bun test (Jest-compatible API, ~4x Jest) |
| TypeScript | Needs tsx / ts-node / tsc compile step | Native, no config needed |
| Bundler | External (webpack / esbuild / rollup / vite) | bun build (esbuild-API-compatible) |
| Ecosystem maturity | 16 years, every package tested | 3 years, ~99% npm compat with edge cases |
| Cloudflare Workers | Workers Node compat layer is solid | Not a Workers runtime; use for local dev |
| Native addons (node-gyp) | Universal support | Mostly works, some edge cases |
| Production track record | Massive (Netflix, Uber, LinkedIn, etc.) | Growing fast (Vercel uses internally, many startups) |
| "Just works" for developer ergonomics | Five separate tools to configure | One tool, opinionated defaults |
node-gyp native addons — check compatibility per-packageworker_threads, vm, deep clusterLocal development on Bun, production on Node — or even local on Bun, production on Workers. Bun gives you the fast install + native TS + fast tests during development; Node (or Workers) handles the production runtime where the long compatibility tail matters. Only the Bun-specific APIs (Bun.serve, Bun.write, Bun.file) need a fallback; everything else is portable. The package.json stays compatible with both.
Bun is the runtime layer beneath modern JavaScript projects, which means it touches almost every other tech sub-tree — databases (Drizzle works on both Bun and Node), edge platforms (Workers isn't Bun but Node-compat is), serverside-rendered frameworks (most Vite/Next.js dev experiences benefit from bun dev), and the MCP / agent ecosystem (Bun's startup speed is a real advantage for spawning short-lived agent processes).
drizzle-kit) noticeably snappier in development.Bun's documentation is unusually good for a young runtime — the docs site is the right place to start. The GitHub repo is where the actual development happens; the changelog is where you find out what just broke or just got faster.