Bun vs Node: Why We Switched Our Runtime
The decision to switch from Node.js to Bun. Performance benchmarks, compatibility testing, and lessons from the migration.
We were happy Node.js users for years. Then we tried Bun.
Three weeks later, our entire stack runs on Bun.
The Promise
Bun promises:
- Faster execution: Up to 4x faster than Node
- Faster startup: Sub-millisecond cold starts
- Native bundling: No webpack/esbuild needed
- Native testing: Jest-compatible test runner
- Node compatibility: Drop-in replacement
But promises are cheap. We tested.
Benchmark Results
Installation Speed
# Node.js project (1,200 dependencies)
npm install # 45 seconds
pnpm install # 22 seconds
bun install # 4 secondsWinner: Bun (11x faster than npm)
Script Execution
# Simple script
node script.js # 180ms
bun run script.js # 45ms
# TypeScript (no pre-compile)
ts-node script.ts # 1,200ms
bun run script.ts # 60msWinner: Bun (4x for JS, 20x for TS)
HTTP Server Performance
// Simple HTTP server benchmark
// 10,000 requests, keep-alive
// Node.js (Express)
Requests/sec: 12,000
Latency avg: 8ms
// Bun (Bun.serve)
Requests/sec: 85,000
Latency avg: 1msWinner: Bun (7x more requests)
Build Time
# Build React app
npm run build # 28 seconds
bun run build # 12 secondsWinner: Bun (2.3x faster)
Test Suite
# 500 tests
npm test # 18 seconds
bun test # 5 secondsWinner: Bun (3.6x faster)
Compatibility Testing
What Worked Immediately
- Express.js ✅
- Hono ✅
- Prisma ✅
- Drizzle ✅
- React/Next.js ✅
- TypeScript ✅
- Most npm packages ✅
What Required Changes
Native modules: Some needed recompilation
bun add sharp # Handles automaticallyNode-specific APIs: Some edge cases
// Node
process.nextTick(callback);
// Bun (works, but setImmediate is preferred)
setImmediate(callback);File watching: Different behavior in some cases
// Use Bun's native watch
import { watch } from "fs";
// vs Bun-native
Bun.file(path).watch(callback);What Didn’t Work
- A few native modules with Node-specific bindings
- Some obscure Node.js APIs
- Certain worker thread patterns
Overall compatibility: ~95% of our code worked unchanged.
Migration Process
Step 1: Test Compatibility
# Replace node_modules
rm -rf node_modules package-lock.json
bun install
# Run tests
bun testStep 2: Update Scripts
// package.json
{
"scripts": {
"dev": "bun run --watch src/index.ts",
"build": "bun build src/index.ts --outdir dist",
"test": "bun test",
"start": "bun run dist/index.js"
}
}Step 3: Update CI/CD
# GitHub Actions
- uses: oven-sh/setup-bun@v1
with:
bun-version: latest
- run: bun install
- run: bun test
- run: bun run buildStep 4: Docker Updates
FROM oven/bun:1 as builder
WORKDIR /app
COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile
COPY . .
RUN bun run build
FROM oven/bun:1-slim
COPY --from=builder /app/dist /app
CMD ["bun", "run", "/app/index.js"]Real-World Impact
Developer Experience
| Metric | Before (Node) | After (Bun) |
|---|---|---|
| Install time | 45s | 4s |
| Dev server start | 3s | 0.5s |
| Hot reload | 500ms | 100ms |
| Test suite | 18s | 5s |
| Build time | 28s | 12s |
Total daily time saved per developer: ~15 minutes
Production Performance
| Metric | Before (Node) | After (Bun) |
|---|---|---|
| Cold start | 800ms | 150ms |
| Requests/sec | 12,000 | 85,000 |
| P99 latency | 45ms | 8ms |
| Memory usage | 180MB | 120MB |
Cost Impact
- Fewer servers needed: 4x throughput per instance
- Less compute time: Faster cold starts in serverless
- Developer productivity: Faster iteration cycles
Gotchas We Discovered
1. Bun’s Bundler Is Young
For complex bundling, we still use esbuild in some places.
2. Error Messages Differ
Stack traces look different. Takes adjustment.
3. Some Node.js Patterns Don’t Translate
// Node pattern that works differently
const module = require(`./dynamic/${name}`);
// Bun prefers
const module = await import(`./dynamic/${name}`);4. Lock File Differences
bun.lockb is binary (faster), but you lose diff visibility.
When to Stick with Node
- Heavy native module usage
- Edge cases with Node.js compatibility
- Team unfamiliar with Bun ecosystem
- Risk-averse production environments
Our Recommendation
For new projects: Start with Bun. The benefits are significant.
For existing projects: Test compatibility, migrate incrementally.
For production: We’ve run Bun in production for 6 months with zero issues.
Bun powers our development workflow. For the full tech stack, see Building a 40+ Platform Ecosystem.