---
title: "Bun Package Manager: Complete Guide vs NPM, Yarn & PNPM"
description: "Bun package manager is up to 30x faster than npm. This 2026 guide covers installing Bun, migrating from npm/yarn/pnpm, and using bun.lock, bun outdated & more."
date: 2026-07-21
categories: ["tools"]
tags: ["bun","npm","yarn"]
---

import { Picture } from "astro:assets";
import YouTubeEmbed from "../../components/widgets/YouTubeEmbed.astro";
import Notice from "../../components/widgets/Notice.astro";
import ListCheck from "../../components/widgets/ListCheck.astro";
import Accordion from "../../components/widgets/Accordion.astro";
import Tabs from "../../components/widgets/Tabs.astro";
import Tab from "../../components/widgets/Tab.astro";
import img1 from "../../assets/images/24/02/bun-speed.webp";

Bun is a JavaScript runtime and package manager built from scratch with Zig and JavaScriptCore. It aims to be a drop-in replacement for NPM, Yarn, and PNPM with significantly faster install times and a simpler toolchain. Since this article was first published in February 2024, Bun has gained full Windows support, a new text-based lockfile, and was acquired by Anthropic in December 2025.

<Notice type="info" title="Updated July 2026">
- **Native Windows support**: Bun 1.1 (April 2024) added full Windows support passing 98% of the test suite. No WSL needed.
- **Text-based lockfile**: Bun 1.2 (January 2025) made `bun.lock` (human-readable JSONC) the default, replacing the binary `bun.lockb`.
- **Anthropic acquired Bun** (December 2025): Bun now powers Claude Code and the Claude Agent SDK. Long-term viability concerns from the original article are largely resolved.
- **New commands**: `bun outdated`, `bun why`, `bun audit`, `bun update --interactive`, `bun publish`, and `bun patch` are all production-ready.
</Notice>

## Why switch to Bun: bun vs npm, yarn & pnpm compared

The core argument for switching is speed. Bun installs packages in parallel using multiple threads, caches results globally, and can be **up to 30x faster** than npm on cold installs. On warm installs (cache populated, lockfile present), the difference is even wider.

But speed is not the only reason. Here's what you get:

<ListCheck>
<ul>
<li>Up to 30x faster installs (cold) and near-instant warm installs</li>
<li>Native Windows, macOS, and Linux support, no WSL needed</li>
<li>Text-based `bun.lock` that diffs cleanly in git PRs</li>
<li>Auto-migration from `package-lock.json`, `yarn.lock`, and `pnpm-lock.yaml`</li>
<li>Built-in test runner, bundler, and package publisher</li>
<li>Backed by Anthropic, powers Claude Code and the Claude Agent SDK</li>
</ul>
</ListCheck>

### Speed comparison: npm vs pnpm vs Bun

Bun's own benchmarks claim "up to 30x faster" installs than npm. That number is from their marketing, not an independent source. In practice, the gap depends on your project size, network, and cache state.

The [pnpm.io/benchmarks](https://pnpm.io/benchmarks) page (daily-updated) compares npm, pnpm, Yarn Classic, and Yarn PnP, but does not include Bun. For npm vs pnpm, the benchmark shows pnpm is roughly 2-3x faster than npm on clean installs. Bun's real-world advantage over npm is typically 5-15x, depending on the scenario.

Your results will vary. Test on your own project. See the real-world benchmark below for one data point.

When this article was first published, some developers questioned Bun's long-term viability, drawing parallels with Yarn's rise and fall. The Anthropic acquisition in December 2025 has largely settled that debate. Bun is no longer a side project. It's the runtime behind Claude Code.

You can check [how to migrate Astro to Bun on CloudFlare](https://www.bitdoze.com/migrate-astro-bun/) for a real migration walkthrough.

## How to install Bun on macOS, Linux & Windows

Bun installs on all major platforms. Below are the current recommended methods.

<Tabs>
<Tab name="macOS">
```sh
brew install oven-sh/bun/bun
```
The old two-step `brew tap oven-sh/bun` + `brew install bun` still works, but the single command above is preferred.
</Tab>
<Tab name="Linux">
```sh
curl -fsSL https://bun.sh/install | bash
```

<Notice type="info" title="Linux prerequisites">
The `unzip` package is required. Install it with `sudo apt install unzip` (Debian/Ubuntu) or the equivalent for your distro. Kernel 5.6+ is recommended; minimum is 5.1. Bun gracefully degrades on kernels as old as 3.10 (RHEL 7) but with reduced performance. Check your kernel with `uname -r`.
</Notice>
</Tab>
<Tab name="Windows">
```powershell
powershell -c "irm bun.sh/install.ps1 | iex"
```
Requires Windows 10 version 1809 or later. Full native support, no WSL needed. Bun on Windows passes 98% of its own test suite.
</Tab>
<Tab name="npm (cross-platform)">
```sh
npm install -g bun
```
Quick option if you already have Node.js and npm installed. Works on all platforms.
</Tab>
</Tabs>

### Verify installation

```sh
bun --version
# Should print something like: 1.3.14

bun --revision
# Prints the build revision (useful for bug reports)
```

To self-update Bun:

```sh
bun upgrade
```

<Notice type="warning" title="CPU compatibility">
Standard Bun builds require AVX2 instructions (Haswell-era Intel, Excavator-era AMD or newer). On older CPUs you'll get an "Illegal Instruction" error. If that happens, install the baseline build: `curl -fsSL https://bun.sh/install | bash -s -- --baseline`.
</Notice>

If `bun` is not found after install, make sure `~/.bun/bin` is in your PATH. Add this to your shell config:

```sh
# bash/zsh, add to ~/.bashrc or ~/.zshrc
export BUN_INSTALL="$HOME/.bun"
export PATH="$BUN_INSTALL/bin:$PATH"
```

For Fish shell, see [best Fish shell plugins and tools](https://www.bitdoze.com/best-fish-shell-plugins/) for PATH setup guidance.

If you need Node.js installed alongside Bun, see [how to install Node.js using NVM](https://www.bitdoze.com/install-nodejs-using-nvm-macos-ubuntu/).

## Bun lockfile: bun.lock vs bun.lockb

Before Bun 1.2 (January 2025), the default lockfile was the binary `bun.lockb`. It worked, but you couldn't diff it in git, couldn't review lockfile changes in PRs, and merge conflicts were impossible to resolve by hand.

**Bun 1.2 made `bun.lock` the default.** It's a text-based JSONC file that diffs and merges like any other source file.

<Notice type="info" title="Migrating from bun.lockb">
```sh
bun install --save-text-lockfile --frozen-lockfile --lockfile-only
# Then remove the old binary lockfile
rm bun.lockb
```
After this, `bun install` will maintain `bun.lock` going forward. The binary `bun.lockb` is still supported for backward compatibility, but there's no reason to keep it.
</Notice>

**Verify:** `ls bun.lock` should exist. Run `git diff bun.lock`. You should see readable JSONC output.

If `bun install` still generates `bun.lockb` instead of `bun.lock`, check `bunfig.toml` for `saveTextLockfile = false` or upgrade your Bun version.

## How to migrate from NPM, Yarn, or PNPM to Bun

Since Bun 1.1, `bun install` can auto-migrate from existing lockfiles. But the cleanest approach is still to remove the old lockfile first, then let Bun generate a fresh `bun.lock`.

<Tabs>
<Tab name="From npm">
```sh
rm package-lock.json
bun install
```
</Tab>
<Tab name="From yarn">
```sh
rm yarn.lock
bun install
```
</Tab>
<Tab name="From pnpm">
```sh
rm pnpm-lock.yaml
bun install
```
</Tab>
</Tabs>

**Verify:** `ls bun.lock` should exist, `ls node_modules` should show installed packages, and `bun run build` (or your project's build command) should succeed.

If you run into issues with native addons, check `bun pm untrusted`. Bun blocks lifecycle scripts (postinstall, etc.) by default since Bun 1.1. See the troubleshooting section below for how to trust packages.

For a full Astro migration walkthrough, see [migrate Astro to Bun on CloudFlare](https://www.bitdoze.com/migrate-astro-bun/).

## Bun command reference: essential commands & npm equivalents

### Core commands

| Bun Command | npm Equivalent | Purpose |
|---|---|---|
| `bun install` | `npm install` | Install all dependencies from package.json |
| `bun add <pkg>` | `npm install <pkg>` | Add a new package |
| `bun add <pkg> --dev` | `npm install <pkg> --save-dev` | Add a dev dependency |
| `bun remove <pkg>` | `npm uninstall <pkg>` | Remove a package |
| `bun run <script>` | `npm run <script>` | Execute a script from package.json |
| `bun outdated` | `npm outdated` | Show outdated packages |
| `bun update <pkg>` | `npm update <pkg>` | Update within semver range |
| `bun update <pkg> --latest` | `npm install <pkg>@latest` | Update to latest version |
| `bun update --interactive` | *(no built-in equivalent)* | Interactive picker (Bun 1.3+) |
| `bun why <pkg>` | `npm explain <pkg>` | Explain why a package is installed |
| `bun audit` | `npm audit` | Scan for security vulnerabilities |
| `bun publish` | `npm publish` | Publish a package to npm |
| `bun patch <pkg>` | `npx patch-package` | Patch a dependency |
| `bun pm pack` | `npm pack` | Create a tarball for publishing |

### Checking for outdated packages

```sh
bun outdated
# Shows all outdated packages with current and latest versions

bun outdated --filter '@myorg/*'
# Filter to specific scope
```

### Updating packages

```sh
bun update <package>            # Update within semver range
bun update <package> --latest   # Update to latest, ignoring semver
bun update                      # Update all deps within semver range
bun update --interactive        # Pick which deps to update interactively
```

For a deeper dive into updating packages with Bun, see [how to update Node packages with Bun](https://www.bitdoze.com/bun-update-packages/) and [how to update all Node.js dependencies](https://www.bitdoze.com/nodejs-update-dependencies/).

### Security and trust

<Notice type="warning" title="Lifecycle scripts are blocked by default">
Since Bun 1.1, postinstall and other lifecycle scripts from packages are blocked by default for security. This is a good thing (it prevents supply chain attacks), but it means some packages (like native addons) won't build until you trust them.
</Notice>

```sh
bun pm untrusted          # List packages with blocked lifecycle scripts
bun pm trust <package>    # Trust a specific package
bun pm trust --all        # Trust all packages (use with caution)
```

## Real-world benchmark: Bun install speed test

I ran this test on the bitdoze.com Astro blog. The project has a typical mix of dependencies. Here are the raw numbers:

**NPM:**

```sh
➜  bitdoze-astro-bkw git:(main) time npm install

npm install  13.22s user 3.82s system 23% cpu 1:13.89 total

npm run build  55.96s user 3.41s system 126% cpu 46.776 total
```

**Bun:**

```sh
➜  bitdoze-astro-bkw git:(main) time bun install

bun install  10.83s user 2.14s system 121% cpu 10.694 total

bun run build  55.30s user 3.34s system 126% cpu 46.181 total
```

<Picture src={img1} alt="Bun install speed benchmark: 11 seconds vs npm's 73 seconds, 7x faster package installation" />

**Results:** Bun install took ~11 seconds. npm install took 73 seconds. That's **7x faster** for this project. The build process was identical. The same Astro build runs at the same speed regardless of which package manager installed the dependencies.

This was tested in February 2024 and re-verified in July 2026. Bun 1.2+ is roughly 30% faster than the version originally tested, so the gap has likely widened for new installs. For a look at how Astro 7 improved build speeds, see [Astro 7 benchmark: build times cut in half](https://www.bitdoze.com/astro-7-faster-builds/). You can also [optimize Astro build speeds](https://www.bitdoze.com/astro-ssg-build-optimization/) further with SSG-specific tweaks.

Your results will vary based on project size, dependency count, hardware, and network conditions.

## Bun in Docker & CI/CD: production best practices

### Dockerfile example

Bun provides official Docker images. Use `oven/bun:alpine` for the build stage (smaller, musl-based) and `oven/bun:distroless` for the runtime (minimal attack surface):

```dockerfile
FROM oven/bun:alpine AS builder
WORKDIR /app
COPY package.json bun.lock ./
RUN bun install --frozen-lockfile

FROM oven/bun:distroless AS runtime
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY . .
EXPOSE 3000
CMD ["bun", "run", "start"]
```

To skip devDependencies in production:

```sh
bun install --frozen-lockfile --production
```

### CI/CD with GitHub Actions

<Notice type="info" title="Always use --frozen-lockfile in CI">
`bun install --frozen-lockfile` fails if `bun.lock` is out of date. This catches lockfile drift before it reaches production. Run `bun install` locally and commit the updated lockfile if CI fails on this step.
</Notice>

```yaml
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: oven-sh/setup-bun@v2
        with:
          bun-version: latest
      - run: bun install --frozen-lockfile
      - run: bun run build
      - run: bun test
```

Common failure: if `--frozen-lockfile` fails, it means your `bun.lock` doesn't match `package.json`. Run `bun install` locally, commit the lockfile, and push.

For more Docker commands and patterns, see [top Docker commands you must know](https://www.bitdoze.com/docker-commands/).

## Bun usage examples

### Add and remove packages

```sh
# Add packages
bun add tailwindcss autoprefixer postcss

# Add a dev dependency
bun add prettier --dev

# Remove a package
bun remove tailwindcss
```

### Run scripts

```sh
bun run dev      # Run the "dev" script from package.json
bun run build    # Run the "build" script
bun run test     # Run the "test" script
```

For monorepos with workspaces, run scripts in specific packages:

```sh
bun run --filter @myorg/web build
```

### Check and update dependencies

```sh
# See what's outdated
bun outdated

# Update interactively (Bun 1.3+)
bun update --interactive

# Update a specific package to latest
bun update tailwindcss --latest
```

### Audit for vulnerabilities

```sh
bun audit
# Scans dependencies for known security vulnerabilities
```

## Troubleshooting common Bun issues

<Accordion label="&quot;Illegal Instruction&quot; error on startup" group="troubleshooting">
Your CPU lacks AVX2 support (pre-Haswell Intel, pre-Excavator AMD). Bun's standard builds require AVX2.

**Fix:** Install the baseline build that works on older CPUs:
```sh
curl -fsSL https://bun.sh/install | bash -s -- --baseline
```
Or on Windows:
```powershell
powershell -c "irm bun.sh/install.ps1 -- --baseline | iex"
```
</Accordion>

<Accordion label="bun: command not found" group="troubleshooting">
`~/.bun/bin` is not in your PATH.

**Fix:** Add to your shell config:
```sh
# bash (~/.bashrc) or zsh (~/.zshrc)
export BUN_INSTALL="$HOME/.bun"
export PATH="$BUN_INSTALL/bin:$PATH"
```
Then reload: `source ~/.bashrc` (or `source ~/.zshrc`).

For Fish shell, add to `~/.config/fish/config.fish`:
```fish
set --export BUN_INSTALL "$HOME/.bun"
set --export PATH "$BUN_INSTALL/bin" $PATH
```
</Accordion>

<Accordion label="Lifecycle scripts blocked (postinstall failing)" group="troubleshooting">
Bun blocks lifecycle scripts (postinstall, preinstall, etc.) by default since Bun 1.1. This is a security measure against supply chain attacks.

**Fix:**
```sh
bun pm untrusted          # See which packages are blocked
bun pm trust <package>    # Trust a specific package
bun pm trust --all        # Trust all (use with caution)
```
Alternatively, add to `package.json`:
```json
{
  "trustedDependencies": ["package-name"]
}
```
</Accordion>

<Accordion label="Kernel too old on Linux" group="troubleshooting">
Bun requires kernel 5.1 minimum, with 5.6+ recommended. It will attempt to run on kernels as old as 3.10 (RHEL 7) with reduced functionality.

**Check your kernel:**
```sh
uname -r
```
If you're on an older kernel, consider upgrading or using a newer distro. On shared hosting or managed VPS, you may not control the kernel, check with your provider.
</Accordion>

<Accordion label="Private registry not working" group="troubleshooting">
Since Bun 1.2, Bun reads `.npmrc` files (`$HOME/.npmrc` and project-level `.npmrc`). You can also configure registries in `bunfig.toml`:

```toml
[install.scopes]
"@myorg" = { url = "https://registry.myorg.com", token = "npm_..." }
```

Or use `.npmrc`:
```
//registry.myorg.com/:_authToken=npm_...
@myorg:registry=https://registry.myorg.com
```
</Accordion>

<Accordion label="How to roll back to npm" group="troubleshooting">
If Bun is causing issues and you need to revert:

```sh
rm -rf node_modules bun.lock
npm install
```

This restores your npm setup. If you kept `package-lock.json` as a backup, you'll get your exact previous dependency tree back. If not, npm will generate a new one from `package.json`.
</Accordion>

## Frequently asked questions

<Accordion label="Is Bun ready for production?" group="faq">
For package management, yes. `bun install` is stable, fast, and used in production CI/CD pipelines. For the Bun runtime (replacing Node.js), maturity varies by use case. Most teams start by using Bun as a package manager only, running their app on Node.js.

The Anthropic acquisition (December 2025) is a strong signal. Bun powers Claude Code and the Claude Agent SDK, which means Anthropic has a direct business interest in Bun's reliability.
</Accordion>

<Accordion label="Does Bun work with private registries?" group="faq">
Yes. Since Bun 1.2, Bun reads `.npmrc` files for auth tokens and scoped registry configuration. It also supports `bunfig.toml` for Bun-specific configuration. Both `$HOME/.npmrc` (global) and project-level `.npmrc` are supported.
</Accordion>

<Accordion label="Can I use Bun in monorepos?" group="faq">
Yes. Bun supports npm-compatible workspaces. Bun 1.3 added `--linker=isolated` for pnpm-style strict dependency resolution and catalogs for centralized version management across packages.

For simple monorepos (a few packages), Bun works well. For complex monorepos with deep dependency trees and strict hoisting requirements, pnpm's mature tooling may still be safer.
</Accordion>

<Accordion label="How does Bun handle disk usage?" group="faq">
Bun uses a global cache at `~/.bun/install/cache` but creates full `node_modules` copies in each project. This means pnpm (which uses hard links) saves more disk space when you have many projects on the same machine.

If disk space is tight and you run dozens of projects, pnpm is the better choice for disk efficiency. For a handful of projects, the difference is negligible.
</Accordion>

<Accordion label="Should I use bun.lockb or bun.lock?" group="faq">
Use `bun.lock`. It's been the default since Bun 1.2, it's human-readable, and it diffs properly in git. `bun.lockb` is only relevant if you're on Bun 1.1 or earlier, or if you explicitly set `saveTextLockfile = false` in `bunfig.toml`. There's no reason to choose the binary format on a modern Bun version.
</Accordion>

## Conclusion

Bun as a package manager is production-ready and significantly faster than npm, yarn, or pnpm for install times. The text-based `bun.lock`, full Windows support, and Anthropic backing have addressed the main concerns from when this article was first published.

My recommendation: try Bun as a package manager on your next project. Remove the old lockfile, run `bun install`, and see how it feels. You can always [roll back](#troubleshooting-common-bun-issues) if something breaks.

The runtime side (replacing Node.js) is a separate decision with more nuance. But for `bun install` vs `npm install`? There's no contest.

<YouTubeEmbed
  url="https://www.youtube.com/embed/BsnCpESUEqM"
  label="Bun Video Presentation"
/>