Install

# Linux / macOS / WSL
curl -fsSL https://bun.sh/install | bash

# macOS via Homebrew
brew install oven-sh/bun/bun

# Windows (native, since Bun 1.1)
powershell -c "irm bun.sh/install.ps1 | iex"

# Or as an npm global, if Node is already around
npm install -g bun

After install:

bun --version
bun upgrade           # upgrade Bun itself

Running an existing Node project

For the majority of CommonJS/ESM Node apps, replace node with bun:

bun run index.js
bun run start         # run the "start" script in package.json
bun src/server.ts     # TypeScript runs directly — no tsc, no ts-node

TypeScript and JSX are first-class: Bun strips types and transpiles on the fly. There is no separate tsc build step for running.

bun install vs npm install

bun install is the package-manager half of the binary. It reads the same package.json as npm and writes a binary lockfile (bun.lock) that's deterministic and roughly an order of magnitude smaller than package-lock.json.

bun install                       # install dependencies from package.json
bun add fastify                   # add a runtime dep
bun add -d @types/node            # dev dep
bun remove some-old-pkg
bun update                        # update within ranges
bun outdated                      # show outdated

It honours npm config (.npmrc) for registries, scopes, and auth tokens — private registries work out of the box. The disk layout uses a global content-addressed cache + hardlinks into the project's node_modules, so multiple projects sharing a dependency don't pay the disk cost twice.

Bun lockfile and npm lockfile can coexist

Bun ignores package-lock.json when bun.lock is present and vice versa. During migration, you can keep both committed, let CI run on Bun, and let local developers stay on npm until they switch. Long-term, delete one.

Bundling: bun build

Bun ships its own bundler, comparable to esbuild for speed:

# Bundle a server for production
bun build ./src/server.ts \
    --outdir ./dist \
    --target bun \
    --minify

# Bundle for the browser
bun build ./src/index.tsx \
    --outdir ./public \
    --target browser \
    --minify \
    --splitting

# Build a standalone single-binary executable (Bun runtime + your app)
bun build ./src/cli.ts --compile --outfile mycli

--compile is genuinely useful: it produces a single ~50–90 MB binary that includes the Bun runtime and your bundled app — deployable to any glibc Linux box without installing Node or Bun separately.

Tests: bun test

Bun has a Jest-compatible test runner built in — no Jest install, no jest.config.js wrangling. Files matching *.test.ts / *.spec.ts are auto-discovered.

// math.test.ts
import { test, expect } from "bun:test";

test("adds", () => {
  expect(2 + 2).toBe(4);
});
bun test                       # run all tests
bun test --watch
bun test --coverage
bun test --bail                # stop on first failure

The runner supports lifecycle hooks (beforeAll/beforeEach/etc), snapshots, mocking, and ESM–in–tests without configuration.

Workspaces (monorepos)

package.json in the repo root with workspaces works the same as npm/Yarn workspaces:

{
  "name": "my-monorepo",
  "private": true,
  "workspaces": ["packages/*", "apps/*"]
}
bun install                            # install all workspace deps
bun run --filter "@scope/app-web" dev  # run "dev" in one workspace
bun run --filter "*" build             # run "build" in every workspace

Production deployment

Three reasonable shapes:

  1. Run Bun directly on the server: install Bun, copy source, bun install --production --frozen-lockfile, bun run start under systemd. Same idea as running Node.
  2. Bundle and compile: bun build --compile on a CI runner, ship one binary to the server, run it under systemd. No Node.js / Bun install needed on the target.
  3. Container image based on oven/bun:1-distroless:
FROM oven/bun:1 AS deps
WORKDIR /app
COPY package.json bun.lock ./
RUN bun install --frozen-lockfile --production

FROM oven/bun:1-distroless
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
USER nonroot
CMD ["bun", "src/server.ts"]

What still isn't 100% Node-compatible

As of Bun 1.2 / 1.3 in 2026, the long tail of incompatibilities is small but real. Areas to test before swapping in production:

  • Some native modules built against V8's ABI — Bun ships a Node-API (N-API) compatibility layer, but very old or odd native modules occasionally need a rebuild.
  • Edge-case Node clustering / worker-thread behaviours, particularly around cluster.fork stdio.
  • The node:vm module — partial; if you do code-eval-style work, test specifically.
  • Some monitoring / APM agents (DataDog, New Relic) depend on V8 internals; check their Bun support before relying on them.

For straightforward HTTP servers (Express/Fastify/Hono), CLI tools, build scripts, and TypeScript apps, Bun runs them today with no porting. Start there; expand outward as the ecosystem catches up.