Bun 1.2 Replaces Node.js Tooling in 20 Minutes: Install Speeds Jump 15x, Tests Run 3x Faster
If your project takes a minute for every
npm install, over ten seconds for each test run, and leaves you staring at the terminal waiting for the dev server to start — you should try Bun. This article skips the hype and directly migrates a real project from Node.js to Bun, laying out the migration steps, the pitfalls encountered, and whether it's right for your project.
What is Bun, and why try it in 2026
If you don't know Bun yet — it's a JavaScript runtime written in Zig, designed as a drop-in replacement for Node.js.
Its relationship to Node.js can be summed up in one sentence:
Bun is to Node.js what Vite is to Webpack — it does the same job, but much faster.
It's not just a runtime, but an all-in-one toolkit:
| Function | Node.js Ecosystem | Bun Built-in |
|---|---|---|
| Runtime | Node.js | Bun |
| Package Manager | npm / pnpm / yarn | bun install |
| Bundler | Webpack / Vite / esbuild | bun build |
| Test Framework | Jest / Vitest | bun test |
| TypeScript | Requires ts-node / tsx | Native support, runs .ts directly |
| .env Loading | Requires dotenv package | Native support |
By 2026, Bun has reached version 1.2+, and its stability is far better than two years ago. Most npm packages work out of the box.
Before-and-after migration data
Both Bun's official benchmarks and community tests point to the same conclusion: it's fast, and not just by a little.
Using a medium-sized full-stack project (React + Express, roughly 200 npm dependencies) as a reference:
| Operation | Node.js 20 + npm | Bun 1.2 | Difference |
|---|---|---|---|
install (no cache) |
~40-60s | ~3-5s | 10-15x |
install (cached) |
~10-15s | < 1s | 20x+ |
| Dev server start | ~5-10s | ~1-3s | 3-5x |
| Unit tests (100+ cases) | ~10-15s | ~3-5s | 3x |
| TypeScript compilation | Requires extra ts-node config | Native support | — |
| Cold start an HTTP endpoint | ~200-400ms | ~50-100ms | 3-4x |
Specific numbers depend on your machine and project size, but the order-of-magnitude gap is consistent. You can run a comparison on your own project to see.
Migration steps (get it done in 20 minutes)
Step 1: Install Bun
# macOS / Linux
curl -fsSL https://bun.sh/install | bash
# Verify installation
bun --version
# 1.2.x
Windows users should install via WSL2. Bun's native Windows support is largely complete as of version 1.2.
Step 2: Replace npm with Bun
# Delete node_modules and lock files
rm -rf node_modules package-lock.json yarn.lock pnpm-lock.yaml
# Reinstall dependencies with Bun
bun install
This step generates bun.lockb (Bun's lock file, a binary format much faster than JSON).
Step 3: Replace scripts in package.json
{
"scripts": {
"dev": "bun run --hot src/server.ts",
"build": "bun build src/index.ts --outdir dist",
"test": "bun test",
"start": "bun src/server.ts"
}
}
Key changes:
node src/server.js→bun src/server.ts(runs TypeScript directly, no ts-node needed)nodemon→bun run --hot(built-in hot reload)jest/vitest→bun test(built-in test framework, Jest-compatible API)
Step 4: Run the tests
bun test
bun run dev
Most projects will run fine at this point. If you hit issues, check the pitfall records below.
Pitfall records
Pitfall 1: Some Node.js native modules are incompatible
My project used bcrypt (password hashing), which is a C++ native addon. Bun supports most native addons now, but a specific version of bcrypt had issues.
Solution: Switch to the pure JS implementation bcryptjs.
bun remove bcrypt
bun add bcryptjs
The only code change is the import path:
// Before
import bcrypt from 'bcrypt';
// After
import bcrypt from 'bcryptjs';
The API is identical; no business logic changes needed. The performance gap is negligible in most scenarios.
General principle: If a package has both a C++ native addon version and a pure JS version, prefer the pure JS version when migrating to Bun.
Pitfall 2: __dirname and __filename behavior differences
Bun supports __dirname and __filename, but in ES Module mode, the behavior differs slightly from Node.js.
If your code has this pattern:
import path from 'path';
const configPath = path.join(__dirname, '../config/default.json');
It might error out in Bun's ESM mode.
Solution: Use import.meta instead.
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const configPath = path.join(__dirname, '../config/default.json');
This approach is compatible with both Node.js and Bun.
Pitfall 3: .env no longer needs dotenv
Bun natively supports .env file loading, so the dotenv package is unnecessary.
If your code contains:
import 'dotenv/config';
// or
require('dotenv').config();
You can delete that line in Bun. Bun automatically loads the .env file from the project root.
But note: If you have both .env and .env.local, Bun's loading priority differs from dotenv. Bun's priority is:
.env.local > .env.production / .env.development > .env
Advice: After migration, double-check that all environment variables are loaded correctly.
Pitfall 4: Syntax differences between bun test and Jest
bun test's API is largely compatible with Jest, but there are a few differences:
// Jest syntax (also works in Bun)
expect(fn).toHaveBeenCalledWith(expect.objectContaining({ id: 1 }));
// But this Jest-specific mocking method is not supported:
jest.mock('./database');
// Bun's mocking method:
import { mock } from 'bun:test';
mock.module('./database', () => ({
query: mock(() => Promise.resolve([]))
}));
Solution: If your tests rely heavily on jest.mock, consider keeping Vitest or Jest for testing for now, and using Bun for everything else. Bun can coexist with existing test frameworks.
Which scenarios are suitable for migration, and which are not
| Scenario | Recommended? | Reason |
|---|---|---|
| Personal project / side project | ✅ Strongly recommended | No legacy baggage, enjoy the speed immediately |
| New project | ✅ Recommended | Start from scratch with Bun, avoid migration costs |
| Small to medium full-stack project | ✅ Yes | Most npm packages are compatible |
| Large enterprise project | ⚠️ Proceed with caution | Many native addons, CI/CD needs adaptation |
| Heavy native addon dependency | ❌ Not recommended for now | sharp, canvas, etc. may have compatibility issues |
| Serverless / Edge deployment | ✅ Recommended | Bun's cold start is extremely fast, ideal for serverless |
Migration checklist
Use this checklist to confirm the migration is complete:
-
bun installsucceeds without errors -
bun run devstarts the dev server normally -
bun testpasses all tests -
.envenvironment variables are loaded correctly - All API endpoints pass manual testing
- Build output from
bun buildis correct - CI/CD pipeline's Node.js image is replaced with a Bun image
-
Dockerfilebase image changed tooven/bun:1.2 - All scripts in
package.jsonare updated - Unnecessary packages removed:
dotenv,ts-node,nodemon
Packages you can uninstall after migration
After migrating to Bun, these packages can be removed directly:
bun remove dotenv ts-node tsx nodemon ts-jest
| Package | Reason |
|---|---|
dotenv |
Bun natively supports .env |
ts-node / tsx |
Bun runs TypeScript natively |
nodemon |
bun run --hot has built-in hot reload |
ts-jest |
bun test natively supports TypeScript |
With fewer dependencies, node_modules shrinks a bit more.
Final thoughts
Migrating the whole project took about 20 minutes, and fixing pitfalls added up to just over an hour.
What you get in return is installs that are over ten times faster, startup that is several times faster, and tests that run 3x faster in your daily development. Once you experience this speed gap, there's no going back.
If you're still hesitating, start by trying it on a side project. A 20-minute migration cost with almost zero risk.
What runtime and package manager does your project currently use? Have you considered migrating to Bun? Let's discuss in the comments.