Skip to main content
Technology & EngineeringBun304 lines

Bun Package Manager

Bun as a package manager: bun install, bun add, bun remove, the binary lockfile (bun.lockb), workspace support, overrides, patching, publishing packages, global cache, and comparison to npm, pnpm, and yarn.

Quick Summary10 lines
You are an expert in using Bun as a JavaScript/TypeScript package manager, covering installation workflows, lockfile management, workspace configuration, and migration from other package managers.

## Key Points

- **Using `--force` in CI**: The `--force` flag bypasses the cache and re-downloads everything. Use `--frozen-lockfile` in CI instead to ensure reproducible builds.
- **Running `bun install` without committing the lockfile**: Every team member and CI pipeline should use the same lockfile. Committing `bun.lockb` is not optional.
- **Using `bunx npm` for everything npm does**: Bun handles install, add, remove, and update natively. Only fall back to `bunx npm` for commands Bun does not implement (like `publish` and `audit`).
- **Not using `--frozen-lockfile` in production/CI**: Without this flag, Bun may update the lockfile during install, leading to inconsistent deployments.
skilldb get bun-skills/Bun Package ManagerFull skill: 304 lines
Paste into your CLAUDE.md or agent config

Bun Package Manager — Fast, Compatible Dependency Management

You are an expert in using Bun as a JavaScript/TypeScript package manager, covering installation workflows, lockfile management, workspace configuration, and migration from other package managers.

Core Concepts

Bun's package manager is built into the runtime binary. It uses a binary lockfile (bun.lockb) instead of a text-based one, a global cache with hardlinks to avoid duplicating packages on disk, and parallel network requests for downloads. These design choices make bun install dramatically faster than npm, yarn, or pnpm -- typically 10-30x for cold installs and near-instant for warm installs.

Bun is compatible with the npm registry and reads package.json the same way other package managers do. You can switch an existing project to Bun without changing your dependencies.

Installing Dependencies

# Install all dependencies from package.json
bun install

# Frozen lockfile (CI -- fails if lockfile would change)
bun install --frozen-lockfile

# Production only (skip devDependencies)
bun install --production

# Dry run (show what would be installed)
bun install --dry-run

# Force re-download everything (ignore cache)
bun install --force

# Verbose output for debugging
bun install --verbose

Adding and Removing Packages

# Add a dependency
bun add express

# Add a specific version
bun add express@4.18.2

# Add a dev dependency
bun add -d vitest
bun add --dev @types/node

# Add a peer dependency
bun add --peer react

# Add an optional dependency
bun add --optional fsevents

# Add from GitHub
bun add github:user/repo
bun add github:user/repo#branch

# Add from a tarball URL
bun add https://example.com/package.tgz

# Add from local path
bun add ./packages/my-lib

# Remove a package
bun remove express

# Update packages
bun update           # update all
bun update express   # update one package

The Binary Lockfile (bun.lockb)

Bun uses a binary lockfile rather than JSON or YAML. This is a deliberate performance choice -- binary parsing is faster than text parsing, and the file is smaller.

# View the lockfile contents as YARN-format text (for inspection)
bun bun.lockb

# Generate a text-based yarn.lock alongside bun.lockb
# (useful for tools that need a readable lockfile)
bun install --yarn

# The lockfile should always be committed to version control
git add bun.lockb

The lockfile records exact resolved versions, integrity hashes, and the full dependency tree. When bun.lockb exists and matches package.json, bun install skips resolution entirely and installs from the lockfile -- this is what makes repeated installs nearly instant.

Do not add bun.lockb to .gitignore. Reproducible builds require the lockfile to be shared across the team.

Workspaces

Bun supports npm-style workspaces for monorepo setups:

// package.json (root)
{
  "name": "my-monorepo",
  "workspaces": [
    "packages/*",
    "apps/*"
  ]
}
# Install all workspace dependencies at once
bun install

# Run a script in a specific workspace
bun run --cwd packages/shared build

# Add a dependency to a specific workspace
bun add zod --cwd packages/api

# Add a workspace package as a dependency of another
# In packages/api/package.json:
# "dependencies": { "shared": "workspace:*" }
bun install

Workspace protocol specifiers:

{
  "dependencies": {
    "shared": "workspace:*",
    "utils": "workspace:^1.0.0",
    "core": "workspace:~2.1.0"
  }
}

Bun hoists shared dependencies to the root node_modules by default, similar to npm workspaces. Each workspace package can still have its own node_modules for conflicting versions.

Overrides and Resolutions

Force a specific version of a transitive dependency:

{
  "overrides": {
    "lodash": "4.17.21",
    "webpack>terser": "5.14.0"
  }
}

Bun also supports Yarn-style resolutions:

{
  "resolutions": {
    "lodash": "4.17.21",
    "**/minimist": "1.2.8"
  }
}

Use overrides to fix security vulnerabilities in transitive dependencies or to force consistent versions across a large dependency tree.

Patching Packages

Apply local patches to node_modules packages that persist across installs:

# Generate a patch file for a package
bun patch express

# This opens the package in node_modules for editing
# Make your changes, then:
bun patch --commit express

# This creates a patches/express@4.18.2.patch file
# and adds a patchedDependencies entry to package.json
// package.json after patching
{
  "patchedDependencies": {
    "express@4.18.2": "patches/express@4.18.2.patch"
  }
}

Commit the patches/ directory to version control. Bun applies patches automatically during bun install.

Publishing Packages

# Login to npm registry
bunx npm login

# Publish a package
bunx npm publish

# Publish with tag
bunx npm publish --tag beta

# Publish scoped package as public
bunx npm publish --access public

Bun uses bunx to delegate to npm's publish command since it does not have its own publish implementation. The package is published to the standard npm registry.

Prepare your package for publishing:

{
  "name": "my-library",
  "version": "1.0.0",
  "main": "./dist/index.js",
  "module": "./dist/index.mjs",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "import": "./dist/index.mjs",
      "require": "./dist/index.js",
      "types": "./dist/index.d.ts"
    }
  },
  "files": ["dist"]
}

Global Cache

Bun maintains a global cache of downloaded packages to avoid re-downloading:

# Default cache location
# macOS/Linux: ~/.bun/install/cache
# Windows: %USERPROFILE%\.bun\install\cache

# Clear the cache
bun pm cache rm

# View cache directory
bun pm cache

When Bun installs a package, it downloads it to the global cache and then hardlinks (or copies on Windows) the files into node_modules. This means multiple projects sharing the same dependency version share disk space.

Running Package Binaries

# Run a package binary (like npx)
bunx cowsay "Hello"

# Run without installing permanently
bunx --bun vitest run

# The --bun flag forces the binary to run with Bun runtime
# instead of Node.js (useful for tools that default to Node)
bunx --bun tsx script.ts

Comparison to npm, pnpm, and Yarn

Featurebunnpmpnpmyarn
Install speedFastestSlowestFastMedium
Lockfile formatBinaryJSONYAMLYAML
Disk usageHardlinks from cacheFull copiesContent-addressable storePnP or node_modules
WorkspacesYesYesYesYes
PatchingYesNo (use patch-package)YesYes
Plug'n'PlayNoNoNoYes
AuditVia bunx npm auditBuilt-inBuilt-inBuilt-in

Anti-Patterns

  • Mixing lockfiles: Do not keep both bun.lockb and package-lock.json in the same project. Delete the old lockfile when switching package managers. Having both causes confusion about which is authoritative.
  • Using --force in CI: The --force flag bypasses the cache and re-downloads everything. Use --frozen-lockfile in CI instead to ensure reproducible builds.
  • Ignoring workspace hoisting issues: When two workspace packages need different versions of the same dependency, Bun hoists one and nests the other. If this causes issues, use overrides to force a single version.
  • Running bun install without committing the lockfile: Every team member and CI pipeline should use the same lockfile. Committing bun.lockb is not optional.
  • Using bunx npm for everything npm does: Bun handles install, add, remove, and update natively. Only fall back to bunx npm for commands Bun does not implement (like publish and audit).
  • Not using --frozen-lockfile in production/CI: Without this flag, Bun may update the lockfile during install, leading to inconsistent deployments.

Migration from npm/yarn/pnpm

# Step 1: Remove old lockfile and node_modules
rm -rf node_modules package-lock.json yarn.lock pnpm-lock.yaml

# Step 2: Install with Bun
bun install

# Step 3: Commit the new lockfile
git add bun.lockb
git commit -m "Switch to bun package manager"

# Step 4: Update CI scripts
# Replace "npm ci" with "bun install --frozen-lockfile"
# Replace "npm run build" with "bun run build"
# Replace "npx" with "bunx"

Verify your scripts still work after switching. Most package.json scripts run identically, but scripts that rely on npm-specific lifecycle hooks (like preinstall running before npm install) may need adjustment since Bun supports preinstall, install, postinstall, prepare, and prepublishOnly but may run them in slightly different order for parallel installs.

Install this skill directly: skilldb add bun-skills

Get CLI access →