I Tried Bun 2.0 — Why I Ditched npm for Good
I ran Bun 2.0 through everything — installation, bundler, test runner, and package management. Here's how much faster it actually is compared to npm, what to watch out for when migrating existing projects, and the settings you can apply to real work right away.
April 2026 · GoCodeLab
The JavaScript development ecosystem is full of tools. Package managers, bundlers, transpilers, and test runners all exist as separate pieces. Each has its own config file, and versions need to be managed independently. Starting a new project means repeating this setup every single time — and somehow that became normal.
Bun collapses all four into a single binary. Runtime, package manager, bundler, and test runner are all packed into one bun command. Install it and you can immediately run TypeScript, install packages, build, and run tests. One tool instead of four.
I ran it myself. On a monorepo cold install, npm took 134.2 seconds. Bun took 4.8 seconds. That's a 28x difference. The number alone didn't sink in — but after feeling it firsthand, going back to npm felt uncomfortable.
- Package installation: up to 28x faster than npm (monorepo baseline)
- Testing: 11x faster than Jest (1,500 cases: 44s → 4s)
- Bundler: HTML file as entry point. Supports single-file builds
- DB: PostgreSQL, MySQL, MariaDB, SQLite built-in. No separate driver needed
- Node.js compatibility ~98%
What is Bun
Bun is a JavaScript runtime created by Jarred Sumner. Unlike Node.js, which runs on the V8 engine (the Chrome engine), Bun is built on Apple's JavaScriptCore — the same engine Safari uses. On top of that, the entire runtime was rewritten in Zig. Zig delivers C-level performance with direct memory control. It was a deliberate choice to squeeze out maximum speed.
The core design principle is one thing: consolidate tools. bun alone handles package installation instead of npm, bundling instead of esbuild/webpack, testing instead of Jest, and TypeScript execution instead of ts-node. Fewer packages to install, fewer config files to manage.
Node.js compatibility was a priority from the start. Major packages like Express, Fastify, and Prisma work without modification. v1.0 launched in September 2023. v1.2 added a built-in DB client. v1.3 completed MySQL/MariaDB support and the HTML entry point bundler.
Installation — one command to start
macOS and Linux are done in one line. Windows works with a single PowerShell command. After installation, run bun --version to confirm it's working.
curl -fsSL https://bun.sh/install | bash
# Windows (PowerShell)
powershell -c "irm bun.sh/install.ps1 | iex"
# Verify version
bun --version
TypeScript runs immediately with no extra configuration. Type bun run index.ts and it works without transpilation. No need to install ts-node or tsx separately. Same goes for JSX — .tsx files run directly.
It coexists with existing Node.js projects. Using different runtimes per project causes no conflicts. You can use node in one directory and bun in another without issues. If switching feels risky, starting with just the package manager is a valid first step.
Package management — the difference is tangible
The speed gap comes from system call count. npm makes around 1 million system calls during package installation. Bun makes 165,000. The file I/O handled is fundamentally different. That difference produces the result: 134.2 seconds vs. 4.8 seconds on a monorepo baseline. In CI pipelines that install dependencies on every build, the cumulative effect is significant.
| Metric | npm | pnpm | Bun |
|---|---|---|---|
| Monorepo cold install | 134.2s | 15s | 4.8s |
| React app dependencies | 18s | — | 2s |
| Tests (1,500 cases) | 44s (Jest) | — | 4s |
| Built-in DB client | X | X | O |
The lockfile is bun.lockb — a binary format by default. Being binary makes it inconvenient to read with git diff. If you need a text format, you can switch to bun.lock via bunfig.toml. Either format must be committed to git to pin versions.
The commands are nearly identical to npm. bun add lodash, bun remove lodash, and bun update all work the same way. In CI pipelines, use bun install --frozen-lockfile to install strictly from the lockfile — the equivalent of npm ci.
Bundling — HTML is the entry point
Bun's built-in bundler is esbuild-based. No need to write a separate Webpack config file. A single bun build command handles JS, CSS, and assets in one pass. Configuration complexity is drastically reduced compared to existing bundlers.
The standout feature is that HTML serves as the entry point. Running bun build ./index.html --outdir=dist automatically detects <script> and <link> tags in the HTML. JS, CSS, and image assets are all processed in one shot. No need to specify a separate JS entry point the way Webpack requires.
Adding the --compile flag produces a single executable with all assets inlined. From v1.3, HTML imports come with HMR (Hot Module Replacement) and React Fast Refresh. Pass an HTML file to Bun.serve() and that becomes your dev server. No separate dev server process to spin up.
bun build ./index.html --outdir=dist
# Single executable build
bun build ./index.html --outdir=dist --compile
# Dev server (with HMR, Bun.serve-based)
bun run dev
Test runner — uses the Jest API as-is
bun test provides a Jest-compatible API. describe, test, expect, beforeEach, and afterAll all work. No need to write a jest.config.js — just run bun test. If you were using Jest before, no test files need to be modified.
The speed difference is substantial. With 1,500 test cases, Jest takes 44 seconds. Bun takes 4 seconds. That's 11x faster. On projects with a large test suite, the time saved during local verification before a git push adds up fast. If you use TDD or keep watch mode running, the impact is even more noticeable.
A fake timers compatibility bug with @testing-library/react was fixed in v1.3.5. However, some items like expect.arrayContaining are still not implemented. Before fully migrating an existing Jest project, it's safer to run bun test once and check the results.
bun test
# Run a specific file
bun test src/utils.test.ts
# Watch mode
bun test --watch
Built-in DB client — no driver needed
Since v1.2, PostgreSQL and SQLite clients have been built into the runtime. No need to install pg or better-sqlite3. v1.3 added MySQL and MariaDB support, consolidating everything into the single Bun.SQL API. You can connect directly to a DB without any external driver.
Bun.SQL identifies the database from the connection string. It recognizes postgres://, mysql://, and file:./db.sqlite formats. Swap the connection string and the query code stays the same even when switching databases. No more learning different client APIs for each DB.
const db = new Bun.SQL("postgres://user:pass@localhost/mydb");
// Template literal → automatic parameter binding
const userId = 42;
const result = await db`SELECT * FROM users WHERE id = ${userId}`;
// SQLite (just a different path)
const sqlite = new Bun.SQL("./mydb.sqlite");
Writing queries with template literals automatically binds values as parameters. Even when you write
${userId}, the string is never inserted directly into the query. Injection is blocked without any separate escaping.Migrating from Node.js
A gradual migration is the realistic approach. There is no need to switch everything at once. Replace tools one at a time: package manager → test runner → bundler. If a problem comes up at any step, stop there.
| Node.js tool | Bun command | Notes |
|---|---|---|
npm install | bun install | Lockfile format differs |
npx ts-node file.ts | bun run file.ts | No transpilation needed |
jest | bun test | Check for unimplemented items |
webpack / esbuild | bun build | HTML as entry point |
pg / mysql2 | Bun.SQL | v1.2+, no install needed |
better-sqlite3 | Bun.SQL | v1.2+ |
The first step is replacing the package manager. Run bun install in the folder containing package.json. node_modules gets rebuilt and bun.lockb is created. No code changes required whatsoever. This alone changes CI pipeline times.
Most packages on npm work in Bun without modification. Express, Fastify, Prisma, Drizzle, and tRPC have all been confirmed. The only cases that don't work are packages using C++ bindings (native addons). Before migrating, you can quickly verify with
bun install && bun run start.Decision criteria by use case
Where Bun makes sense depends on the project situation. For new projects, starting with Bun from scratch is the most straightforward path. For existing projects, gradual migration is realistic. CI/CD pipelines are the entry point where you'll see the fastest payoff.
| Situation | Recommendation | Reason |
|---|---|---|
| New project | Strongly recommended | Minimal tooling setup, start immediately with no config files |
| CI/CD pipeline | Recommended | Improve package install speed with zero code changes |
| Existing npm project | Gradual migration | Replace package manager first, expand incrementally |
| Native addon usage | Verify first | Some C++ binding packages are incompatible |
| Enterprise production | Validate before adopting | Ecosystem maturity gap compared to Node.js |
Swapping npm install for bun install in CI/CD carries the lowest risk. The code stays untouched. Only build time shrinks. Running it in the pipeline first before the entire team switches to Bun is also a valid approach.
Limitations and caveats
Bun is still maturing. Node.js has been battle-tested in production for over 15 years. The depth of the ecosystem and the size of the community are significantly different. Native addons — packages using C++ bindings — may not work. It's safer to check your package list before migrating.
Windows support has stabilized compared to early v1.x. PowerShell installation is now supported, but real-world validation cases are fewer than on macOS and Linux. If your team has many Windows development environments, test thoroughly before adopting.
For enterprise production environments, sufficient validation is needed before adoption. The realistic approach is to use it on personal or side projects first, then expand to team projects. There is no need to adopt all of Bun at once. Using only the package manager and keeping everything else as-is is also a perfectly valid choice.
1. Check whether the project uses any native addon packages
2. After
bun install, verify basic behavior with bun run start3. If existing tests are in place, run them once with
bun test4. Commit
bun.lockb to gitThere is no need to adopt all of Bun at once. Swapping the package manager first, then the test runner, then the bundler — migrating one piece at a time is a valid path. If each step has no issues, move on to the next.
FAQ
Q. Can Bun fully replace Node.js?
npm package compatibility is around 98%. Major packages like Express, Fastify, and Prisma work without modification. Only packages using native addons (C++ bindings) need separate verification. Most projects work as-is.
Q. Does bun test fully replace Jest?
Most of the API is compatible. describe, test, expect, and mock functions all work. However, some items like expect.arrayContaining are still not implemented. It is safer to run existing tests once before migrating everything over.
Q. Why does the bun.lockb file need to be committed to git?
bun.lockb is a binary lockfile. Without it, package versions are not pinned. Different team members may end up with different versions installed. If you need a text format, you can switch to bun.lock.
Q. Which databases does Bun SQL support?
As of v1.3, PostgreSQL, MySQL, MariaDB, and SQLite are all handled through the single Bun.SQL API. No separate npm driver installation is needed. Swap the connection string and the query code stays the same even after switching databases.
Q. What is the easiest way to switch from an existing npm project to Bun?
Just run bun install in the directory that has a package.json. No code changes needed. Swapping the package manager first to feel the speed difference is the safest entry point. This alone changes CI pipeline times.
There is no need to adopt all of Bun at once. Swapping the package manager first, then the test runner, then the bundler — gradual migration is entirely possible. If you are currently using npm install in CI, switching to bun install today is enough of a start.
- Bun official blog — v1.2, v1.3 release notes
- Bun official docs — installation, API, bundler, test runner
- oven-sh/bun GitHub — source code and issue tracker
Get GoCodeLab Updates
JavaScript, AI tools, and developer productivity insights — personally verified before sharing.
SubscribeThe figures in this article are based on official Bun release notes and benchmark data. Actual performance may vary depending on the environment.
Last updated: April 2026 · GoCodeLab