Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

Bun — User Guide

Generated May 2026 from oven-sh/bun at 9ed6e89

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

Tip

Bun is your all-in-one JavaScript toolkit: runtime, package manager, bundler, and test runner in a single binary. If you're coming from Node.js, drop Bun in and gain dramatically faster installs and startup times with zero config changes.


1. Quick Start

Bun is a fast, all-in-one JavaScript runtime built with performance as a first principle. It runs JavaScript, TypeScript, JSX, and TSX natively without any additional tooling. The package manager installs dependencies up to 25× faster than npm. The bundler replaces webpack and esbuild. The test runner is Jest-compatible. SQLite, PostgreSQL, MySQL, Redis, and S3 clients ship built-in. Shell scripting works via Bun.$. And bun build --compile produces self-contained executables.

Prerequisites:

  • macOS (arm64 or x64), Linux (arm64 or x64, glibc or musl), or Windows (x64 or arm64)
  • No Node.js required — Bun is self-contained

Install in one line:

curl -fsSL https://bun.sh/install | bash

Your first project:

mkdir my-app && cd my-app
bun init
bun run index.ts

bun init scaffolds a minimal TypeScript project with package.json, tsconfig.json, index.ts, .gitignore, and README.md. bun run index.ts executes it immediately — no compile step, no ts-node, no separate transpiler.

Expected output from bun init:

bun init helps you get started with a minimal project and tries to
guess sensible defaults. Press ^C anytime to quit.

package name (my-app):
entry point (index.ts):

Done! A package.json file was saved in the current directory.
 + index.ts
 + .gitignore
 + tsconfig.json (for editor auto-complete)
 + README.md

To get started, run:
  bun run index.ts

Verify your installation:

bun --version
# 1.3.13

Note

Bun reads tsconfig.json for path aliases and JSX settings automatically. You don't need any additional TypeScript configuration to run .ts files.


2. Installation

Supported Platforms

Platform Architecture Notes
macOS arm64 (Apple Silicon) Primary development platform
macOS x64 (Intel) Fully supported
Linux arm64 glibc and musl
Linux x64 glibc and musl
Windows x64 Full support
Windows arm64 Full support

Installation Methods

curl -fsSL https://bun.sh/install | bash

After install, restart your terminal or run source ~/.bashrc (or ~/.zshrc). The installer adds Bun to ~/.bun/bin and updates your shell's PATH.

npm

npm install -g bun

Useful if you already have Node.js and want a quick install without running a shell script.

Homebrew (macOS and Linux)

brew tap oven-sh/bun
brew install oven-sh/bun/bun

Docker

docker pull oven/bun:latest

Available image variants:

Tag Base Use Case
oven/bun:latest Debian General purpose
oven/bun:alpine Alpine Minimal image size
oven/bun:debian Debian Explicit Debian
oven/bun:debian-slim Debian Slim Smaller Debian footprint
oven/bun:distroless Distroless Production security hardening

Example Dockerfile:

FROM oven/bun:latest
WORKDIR /app
COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile
COPY . .
CMD ["bun", "run", "src/index.ts"]

Scoop (Windows)

scoop install bun

Proto (version manager)

proto install bun

CPUs Without AVX2 Support

Older CPUs that lack AVX2 instructions require the baseline build:

curl -fsSL https://bun.sh/install | bash -s -- --no-avx2

Important

If Bun crashes immediately with an illegal instruction error on startup, your CPU likely lacks AVX2 support. Reinstall using the --no-avx2 flag above.

Upgrading Bun

bun upgrade

This downloads the latest release directly from GitHub. To upgrade to a specific version:

bun upgrade --version 1.3.13

To upgrade to the canary (nightly) build:

bun upgrade --canary

3. Configuration

bunfig.toml

bunfig.toml is Bun's primary configuration file. It can live at the project root (per-project) or at ~/.bunfig.toml (global user defaults). Project-level settings override global ones.

[install]
# Do not install devDependencies
production = false

# Fail if bun.lockb would change (useful in CI)
frozen-lockfile = false

# Default registry
registry = "https://registry.npmjs.org/"

# Default installation backend
# Options: clonefile (default on macOS), hardlink, symlink, copyfile
# backend = "clonefile"

[run]
# Shell used to run package.json scripts
# "bun" = Bun's built-in shell (cross-platform)
# "system" = /bin/sh on Unix, cmd.exe on Windows
shell = "bun"

[test]
# Per-test timeout in milliseconds
timeout = 5000

# Collect coverage by default
coverage = false

# Coverage reporters: "text", "lcov"
coverageReporter = ["text"]

# Directory for coverage output
# coverageDir = "coverage"

Bun also reads tsconfig.json for:

  • Path aliases (compilerOptions.paths) — resolved automatically at runtime
  • JSX settings (compilerOptions.jsx, jsxImportSource) — no build step needed

Environment Variables

Bun automatically loads .env files at startup in this priority order:

  1. .env.local
  2. .env.development (or .env.production when NODE_ENV=production)
  3. .env

Access them via process.env or Bun.env:

// index.ts
console.log(process.env.DATABASE_URL);
console.log(Bun.env.DATABASE_URL); // same thing

Custom env file:

bun run --env-file .env.staging server.ts

Disable env file loading entirely:

bun run --no-env-file server.ts

Tip

Bun supports multiple --env-file flags in a single command, loaded in order. Later files take precedence for duplicate keys.

Per-Registry Authentication (bunfig.toml)

[install.scopes]
"@myorg" = { token = "$NPM_TOKEN", registry = "https://npm.pkg.github.com/" }

For private registries requiring auth, set credentials per scope rather than globally to avoid leaking tokens.


4. Core Usage

Running Scripts

Run a file directly

bun run index.ts
bun index.ts          # shorthand — same result

TypeScript, JSX, and TSX work without any configuration:

bun run app.tsx
bun run utils.js
bun run server.mjs

Run package.json scripts

bun run dev
bun run build
bun run test

Note

bun run is significantly faster at script lookup and startup than npm run. For a project with dozens of scripts, the difference is immediately noticeable.

Watch mode (restart on change)

bun run --watch index.ts

Bun watches the entry file and all imported modules. The process restarts cleanly on any change.

Hot reload (preserve state)

bun run --hot server.ts

Hot reload re-evaluates changed modules without restarting the process. HTTP servers stay up; in-memory state is preserved. Ideal for server development where you don't want to drop connections.

Difference between --watch and --hot:

Mode Process Restart State Preserved Best For
--watch Yes No Scripts, CLIs, workers
--hot No Yes HTTP servers, long-running services

Evaluate inline code

bun --eval 'console.log("hello")'
bun -e 'console.log(process.version)'

# Print the result of an expression
bun --print 'Math.PI * 2'
bun -p '["a","b","c"].join(",")'

Run scripts in workspaces

# Run "build" in all workspaces matching the pattern
bun run --filter "packages/*" build

# Run in parallel (default behavior with --filter)
bun run --filter "*" test --parallel

# Run sequentially
bun run --filter "*" build --sequential

Package Management

Install all dependencies

bun install
bun i          # alias

Key flags:

Flag Purpose
--production / -p Skip devDependencies
--frozen-lockfile Fail if lockfile would change (CI recommended)
--dry-run Show what would be installed without installing
--force / -f Re-download packages even if cached
--no-cache Ignore cache entirely
--global / -g Install to global package store
--save-text-lockfile Save bun.lock (text) instead of bun.lockb (binary)
--backend <mode> clonefile (default macOS), hardlink, symlink, copyfile
--linker <mode> isolated (default) or hoisted (node_modules flat layout)
--omit <type> Omit dev, optional, or peer deps
--lockfile-only Only update lockfile, do not install to node_modules
--trust Mark installed packages as trusted (for lifecycle scripts)
--analyze / -a Show bundle size analysis after install

Add a dependency

bun add react react-dom
bun add --dev typescript @types/node   # devDependency
bun add --optional sharp               # optionalDependency
bun add --peer react                   # peerDependency
bun add --exact react                  # pin to exact version (no ^)
bun add --only-missing react           # add only if not already present
bun a zod                              # alias for add

Remove a dependency

bun remove react
bun rm react     # alias

Update dependencies

bun update                 # update within semver ranges
bun update --latest        # update to latest, ignoring semver ranges
bun update --interactive   # choose per package interactively
bun update --recursive     # update across all workspaces

Check for outdated packages

bun outdated
bun outdated --filter "packages/ui"   # limit to a workspace
bun outdated --recursive              # check all workspaces

Example output:

Package   Current  Update  Latest
react     18.2.0   18.3.1  19.0.0

Security audit

bun audit          # human-readable output
bun audit --json   # machine-readable JSON

Run packages without installing

bunx cowsay "hello"
bun x cowsay "hello"   # alias

# Force running with Bun (instead of Node.js shebang)
bunx --bun tsx script.ts

bunx downloads and caches the package, then runs it. The cache persists across invocations so subsequent runs are instant.

# In the package you want to link
bun link

# In the project that should use it
bun link my-local-package

Bundling

Basic bundle

bun build src/index.ts --outdir dist

Target environments

bun build src/index.ts --outdir dist --target browser   # browser bundle
bun build src/index.ts --outdir dist --target bun       # Bun runtime
bun build src/index.ts --outdir dist --target node      # Node.js runtime

Output formats

bun build src/index.ts --outfile bundle.js --format esm    # ES modules (default)
bun build src/index.ts --outfile bundle.js --format cjs    # CommonJS
bun build src/index.ts --outfile bundle.js --format iife   # IIFE for <script> tags

Code splitting

bun build src/index.ts --outdir dist --splitting

Produces multiple chunks that share code; useful for browser apps with dynamic imports.

Source maps

bun build src/index.ts --outdir dist --sourcemap linked    # separate .map file, linked
bun build src/index.ts --outdir dist --sourcemap inline    # embedded in output
bun build src/index.ts --outdir dist --sourcemap external  # separate .map, not linked
bun build src/index.ts --outdir dist --sourcemap none      # disabled

Minification

bun build src/index.ts --outdir dist --minify                    # all minification
bun build src/index.ts --outdir dist --minify-syntax             # fold constants, simplify
bun build src/index.ts --outdir dist --minify-whitespace         # remove whitespace
bun build src/index.ts --outdir dist --minify-identifiers        # shorten variable names

Compile a standalone executable

bun build src/cli.ts --compile --outfile my-tool
./my-tool   # runs without Bun installed

The resulting binary bundles the Bun runtime and your code. No external dependencies required on the target machine.

Windows executable options:

bun build src/app.ts --compile --outfile app.exe \
  --windows-hide-console \
  --windows-icon assets/icon.ico \
  --windows-title "My App" \
  --windows-publisher "Acme Corp" \
  --windows-version "1.0.0" \
  --windows-description "Does something useful" \
  --windows-copyright "Copyright 2026"

Compile-time autoload control:

# Disable .env autoloading in the compiled binary
bun build src/app.ts --compile --no-compile-autoload-dotenv

# Control other autoloaded files
bun build src/app.ts --compile \
  --no-compile-autoload-bunfig \
  --no-compile-autoload-tsconfig \
  --no-compile-autoload-package-json

Other build flags

Flag Purpose
--external <pkg> Exclude package from bundle (treated as external)
--watch Rebuild on file changes
--metafile Emit metafile.json with bundle analysis
--metafile-md Emit metafile.md (human-readable analysis)
--banner <text> Prepend text to output files
--footer <text> Append text to output files
--public-path <url> URL prefix for asset references
--packages <mode> external (exclude all npm) or bundle (include all)
--env <mode> inline (embed env), prefix* (embed matching), disable
--production Set NODE_ENV=production and enable full minification
--no-bundle Transpile only — no bundling, preserves imports
--keep-names Preserve function/class names after minification
--bytecode Emit bytecode alongside JS for faster cold starts
--define / -d Replace identifiers at build time
--loader / -l Override file loader (e.g., .png:dataurl)

Experimental:

# Bun Bake — full-stack web app bundler (experimental)
bun build --app

# Server Components (experimental)
bun build --app --server-components

Testing

Run all tests

bun test

Bun discovers test files matching **/*.test.{ts,tsx,js,jsx}, **/*.spec.{ts,tsx,js,jsx}, and **/test.{ts,js}.

Filter tests

bun test --test-name-pattern "auth"        # run tests matching regex
bun test -t "should handle errors"         # same, short flag
bun test src/auth.test.ts                  # run specific file

Test options

Flag Purpose
--timeout <ms> Per-test timeout (default 5000)
--update-snapshots / -u Overwrite snapshot files
--bail <n> Stop after n failures
--only Run only tests marked test.only()
--todo Run tests marked test.todo()
--rerun-each <n> Run each test n times (for flakiness detection)
--retry <n> Retry failing tests up to n times
--randomize Randomize test order
--seed <int> Set random seed for reproducible randomization
--pass-with-no-tests Exit 0 when no test files found
--concurrent Run test files concurrently
--max-concurrency <n> Limit concurrent test file execution
--dots Minimal dot-style reporter
--reporter <mode> junit or dots
--reporter-outfile <path> Write reporter output to file
--only-failures Re-run only previously failing tests
--changed <ref> Run tests for files changed since git ref
--path-ignore-patterns <glob> Exclude matching paths

Coverage

bun test --coverage
bun test --coverage --coverage-reporter lcov
bun test --coverage --coverage-dir coverage/

Example output with coverage:

✓ src/math.test.ts > add > handles integers [0.42ms]
✓ src/math.test.ts > add > handles floats [0.12ms]
✓ src/math.test.ts > multiply > basic multiplication [0.08ms]

3 pass, 0 fail

Coverage report:
---------------------|---------|----------|---------|---------|
File                 | % Stmts | % Branch | % Funcs | % Lines |
---------------------|---------|----------|---------|---------|
src/math.ts          |   100   |    100   |   100   |   100   |
---------------------|---------|----------|---------|---------|

Writing tests (Jest-compatible API)

import { describe, it, expect, beforeEach, afterAll, mock, spyOn } from "bun:test";

describe("UserService", () => {
  let service: UserService;

  beforeEach(() => {
    service = new UserService();
  });

  it("creates a user", async () => {
    const user = await service.create({ name: "Alice" });
    expect(user.id).toBeDefined();
    expect(user.name).toBe("Alice");
  });

  it("throws on duplicate email", () => {
    expect(() => service.create({ email: "[email removed]" })).toThrow();
  });
});

Bun's test runner supports: describe, it, test, expect, beforeAll, afterAll, beforeEach, afterEach, mock, spyOn, jest.fn(), jest.mock(), snapshot testing, and test.skip() / test.only() / test.todo().

Other Commands

Command Purpose
bun init Scaffold a new project
bun repl Start an interactive JavaScript REPL
bun exec <script> Execute a shell script file
bun upgrade Upgrade Bun to the latest version
bun pm Package manager utilities (cache, bin paths, etc.)
bun pm cache Manage the package cache
bun pm bin Show path to local bin directory
bun pm ls List installed packages
bun pm trust Manage trusted packages for lifecycle scripts

Global Runtime Flags

These flags work with bun run, bun test, bun build, and direct file execution:

Flag Purpose
--env-file <path> Load environment variables from a specific file
--no-env-file Disable automatic .env loading
--cwd <path> Set working directory
--config / -c <path> Use a specific bunfig.toml
--watch Restart on file changes
--hot Hot reload (preserve state)
--smol Reduce memory usage (slower GC tradeoff)
--preload <file> / -r Run a file before the entry point
--inspect Enable debugger (default: ws://localhost:6499)
--inspect-wait Enable debugger and wait for connection before starting
--inspect-brk Enable debugger and pause on first line
--cpu-prof* CPU profiling options
--heap-prof* Heap profiling options
--if-present Exit 0 if script not found in package.json
--no-install Never auto-install missing modules
--install <mode> auto, fallback, or force module resolution
--port <n> Default HTTP port
--conditions <list> Export condition overrides
--no-macros Disable Bun macros
--prefer-offline Use cache over network
--prefer-latest Prefer latest version when resolving
--max-http-header-size <n> Max HTTP header size in bytes
--dns-result-order <mode> DNS result ordering (ipv4first, verbatim)
--expose-gc Expose global.gc()
--define / -d <k=v> Replace global identifiers
--drop <identifier> Remove identifier references from code
--loader / -l <ext:type> Override file loader
--jsx-* JSX transform options
--no-clear-screen Prevent clearing terminal in watch mode
--unhandled-rejections <mode> throw, warn, ignore
--console-depth <n> Max depth for console.log object inspection
--title <name> Set process title
--zero-fill-buffers Zero-fill Buffer allocations
--use-system-ca Use system certificate store for TLS
--use-openssl-ca Use OpenSSL certificate store
--use-bundled-ca Use Bun's bundled CA certificates
--redis-preconnect Open Redis connection at startup
--sql-preconnect Open SQL connection at startup

Key Runtime APIs

HTTP Server

import { serve } from "bun";

const server = Bun.serve({
  port: 3000,
  fetch(request) {
    const url = new URL(request.url);
    if (url.pathname === "/") {
      return new Response("Hello, Bun!", {
        headers: { "Content-Type": "text/plain" },
      });
    }
    return new Response("Not Found", { status: 404 });
  },
});

console.log(`Listening on http://localhost:${server.port}`);

File I/O

// Read a file
const file = Bun.file("./data.json");
const data = await file.json();
const text = await Bun.file("./readme.txt").text();
const buffer = await Bun.file("./image.png").arrayBuffer();

// Check existence
const exists = await Bun.file("./config.yaml").exists();

// File stats
const stat = await Bun.file("./log.txt").stat();
console.log(stat.size, stat.mtime);

// Write a file
await Bun.write("./output.txt", "Hello, World!");
await Bun.write("./data.json", JSON.stringify({ key: "value" }, null, 2));

// Streaming write
const writer = Bun.file("./large.txt").writer();
writer.write("chunk 1\n");
writer.write("chunk 2\n");
await writer.end();

// Delete a file
await Bun.file("./temp.txt").delete();

Shell Scripting

import { $ } from "bun";

// Run a command, get output
const result = await $`ls -la`.text();
console.log(result);

// Pipe between commands
const count = await $`cat package.json | wc -l`.text();

// Capture stderr separately
const { stdout, stderr } = await $`git status`.quiet();

// Use template literals with variables (safe — no injection)
const filename = "my file.txt";
await $`rm ${filename}`;

// Run in a specific directory
await $`npm install`.cwd("/tmp/my-project");

Database — PostgreSQL/MySQL

import { sql } from "bun";

// Bun reads DATABASE_URL from environment
const users = await sql`SELECT * FROM users WHERE active = ${true}`;

// Transactions
await sql.transaction(async (tx) => {
  await tx`INSERT INTO orders (user_id, total) VALUES (${userId}, ${total})`;
  await tx`UPDATE users SET order_count = order_count + 1 WHERE id = ${userId}`;
});

Format Parsers

// TOML
const config = Bun.TOML.parse(`[server]\nport = 3000`);

// YAML
const yaml = Bun.YAML.parse("name: bun\nversion: 1.3.13");
const yamlStr = Bun.YAML.stringify({ name: "bun" });

// JSONC (JSON with Comments)
const jsoncData = Bun.JSONC.parse(`{ // comment\n "key": "value" }`);

// JSON5
const json5 = Bun.JSON5.parse(`{ key: 'value', /* comment */ }`);
const json5Str = Bun.JSON5.stringify({ key: "value" });

// JSONL (JSON Lines)
const lines = Bun.JSONL.parse(`{"a":1}\n{"b":2}\n`);
// lines = [{ a: 1 }, { b: 2 }]

Markdown Rendering

// Render to HTML
const html = Bun.markdown.html("# Hello\n\nThis is **bold**.");

// Render to ANSI terminal output
const terminal = Bun.markdown.ansi("# Hello\n\nThis is **bold**.");

// Render with options
const rendered = Bun.markdown.render("## Section\n\nContent.");

// Render to React elements
const reactEl = Bun.markdown.react("## Section");

Password Hashing

// Hash a password (uses bcrypt by default)
const hash = await Bun.password.hash("my-secret-password");

// Verify
const isValid = await Bun.password.verify("my-secret-password", hash);
// true

// Explicit algorithm
const bcryptHash = await Bun.password.hash("password", { algorithm: "bcrypt", cost: 10 });
const argon2Hash = await Bun.password.hash("password", { algorithm: "argon2id" });

Non-Crypto Hashing

// Fast hashing for hash tables, checksums
const hash1 = Bun.hash.wyhash("hello world");
const hash2 = Bun.hash.crc32("data");
const hash3 = Bun.hash.xxHash64("large data");
const hash4 = Bun.hash.murmur32("input");
const hash5 = Bun.hash.cityHash32("input");

Terminal String Utilities

Bun.stringWidth("hello");        // 5 (handles unicode/emoji width)
Bun.stripANSI("\x1b[31mred\x1b[0m");  // "red"
Bun.sliceAnsi("\x1b[31mhello\x1b[0m", 0, 3);  // "\x1b[31mhel\x1b[0m"
Bun.wrapAnsi("long text...", 40);  // word-wrap preserving ANSI codes

CSRF Protection

const token = Bun.CSRF.generate();
const isValid = Bun.CSRF.verify(token, storedToken);

Web Workers

const worker = new Worker("./worker.ts", {
  smol: true,           // reduced memory mode
  preload: "./setup.ts", // run before worker code
  env: { API_URL: "https://api.example.com" }, // isolated env
});

worker.postMessage({ task: "compute", data: [1, 2, 3] });
worker.onmessage = (e) => console.log(e.data);

5. Common Tasks

Create and Run a TypeScript Project

mkdir my-service && cd my-service
bun init -y

Edit index.ts:

const greeting = (name: string): string => `Hello, ${name}!`;
console.log(greeting("Bun"));
bun run index.ts
# Hello, Bun!

No tsc, no ts-node, no build step.

Set Up an HTTP Server

// server.ts
Bun.serve({
  port: 3000,
  async fetch(req) {
    const url = new URL(req.url);

    if (req.method === "GET" && url.pathname === "/health") {
      return Response.json({ status: "ok", uptime: process.uptime() });
    }

    if (req.method === "POST" && url.pathname === "/echo") {
      const body = await req.json();
      return Response.json(body);
    }

    return new Response("Not Found", { status: 404 });
  },
});
bun run --hot server.ts
# Listening on http://localhost:3000

Modify server.ts and save — the server reloads without dropping connections.

Install Packages and Manage Dependencies

# Add production dependencies
bun add express zod

# Add development dependencies
bun add --dev @types/express vitest

# Install all deps (e.g., after cloning a repo)
bun install

# Install for CI (fail if lockfile would change)
bun install --frozen-lockfile

# Install production only (skip devDeps)
bun install --production

Bun creates bun.lockb (binary lockfile) or bun.lock (text, with --save-text-lockfile). Commit either to version control for reproducible installs.

Bundle for Production

# Browser bundle with minification
bun build src/index.ts \
  --outdir dist \
  --target browser \
  --minify \
  --sourcemap linked \
  --production

# Check what was emitted
ls dist/
# index.js  index.js.map  metafile.json

With code splitting for a multi-page app:

bun build src/pages/*.ts \
  --outdir dist \
  --target browser \
  --splitting \
  --format esm

Create a Standalone Executable

// cli.ts
const args = process.argv.slice(2);
if (args[0] === "--help") {
  console.log("Usage: my-tool [options]");
  process.exit(0);
}
console.log("Running with args:", args);
bun build cli.ts --compile --outfile my-tool

# Test it — no Bun needed on target machine
./my-tool --help
# Usage: my-tool [options]

# Check the binary
file my-tool
# my-tool: ELF 64-bit LSB executable, x86-64, ...

Run Tests with Coverage

# Run all tests with coverage
bun test --coverage

# CI-friendly: fail if coverage drops below threshold, use lcov for reporting
bun test \
  --coverage \
  --coverage-reporter lcov \
  --coverage-dir coverage \
  --bail 1

Configure default coverage behavior in bunfig.toml:

[test]
coverage = true
coverageReporter = ["text", "lcov"]
coverageDir = "coverage"

Use Hot Reloading During Development

# Start server with hot reload
bun run --hot src/server.ts

# In another terminal, watch your changes take effect live
curl http://localhost:3000/health

For scripts and CLIs where you want a clean restart:

bun run --watch src/worker.ts

Parse Environment Variables from .env Files

# .env
DATABASE_URL=postgresql://localhost/mydb
PORT=3000
NODE_ENV=development
// These are automatically available — no dotenv package needed
const db = Bun.env.DATABASE_URL;
const port = parseInt(Bun.env.PORT ?? "3000");
# Use a different env file
bun run --env-file .env.staging src/server.ts

# Disable env file loading
bun run --no-env-file src/server.ts

Use Bun as a Drop-In Node.js Replacement

For most Node.js projects, Bun works without any changes:

# Replace node with bun
bun src/app.js

# Replace npm scripts
bun run start
bun run dev

# Replace npx
bunx create-react-app my-app

Bun implements the Node.js API (fs, path, http, crypto, stream, etc.) and resolves node:* imports. CommonJS (require()) and ESM (import) both work, and can be mixed in the same project.

Important

Some native Node.js addons (.node files) may not be compatible with Bun. Check the Bun compatibility tracker at bun.sh/nodejs for the current status of any modules you depend on.

Execute Shell Commands with Bun.$

import { $ } from "bun";

// Build pipeline
await $`bun install`;
await $`bun build src/index.ts --outdir dist --minify`;
await $`tar -czf dist.tar.gz dist/`;

// Read output
const branch = await $`git rev-parse --abbrev-ref HEAD`.text();
console.log(`Current branch: ${branch.trim()}`);

// Check exit code without throwing
const result = await $`test -f .env`.quiet().nothrow();
if (result.exitCode !== 0) {
  console.warn("No .env file found, using defaults");
}

// Pipe data in
const json = JSON.stringify({ key: "value" });
await $`cat`.stdin(json).text();

Use Workspaces in a Monorepo

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

# Run a script in all workspaces
bun run --filter "*" build

# Run in specific workspaces
bun run --filter "packages/ui" test
bun run --filter "apps/*" dev --parallel

Each workspace gets its own node_modules/.bin entries, but Bun deduplicates packages at the top level.

Connect to PostgreSQL with the Built-In SQL Client

import { sql } from "bun";

// Set DATABASE_URL in your .env
// DATABASE_URL=postgresql://user:pass@localhost:5432/mydb

// Query
const users = await sql`
  SELECT id, name, email
  FROM users
  WHERE created_at > ${new Date(Date.now() - 86400_000)}
  ORDER BY created_at DESC
  LIMIT 10
`;

// Parameterized inserts
const [newUser] = await sql`
  INSERT INTO users (name, email)
  VALUES (${name}, ${email})
  RETURNING *
`;

console.log(newUser.id);

For MySQL:

const db = new Bun.sql("mysql://user:pass@localhost:3306/mydb");
const rows = await db`SELECT * FROM products LIMIT 5`;

Render Markdown to HTML

const markdown = `
# Getting Started

Install Bun with:

\`\`\`bash
curl -fsSL https://bun.sh/install | bash
\`\`\`

Then run any TypeScript file **directly**.
`;

const html = Bun.markdown.html(markdown);
// Returns fully-rendered HTML string

// In a Bun.serve() handler:
Bun.serve({
  port: 3000,
  fetch() {
    return new Response(html, {
      headers: { "Content-Type": "text/html; charset=utf-8" },
    });
  },
});

6. Troubleshooting

Error: Cannot find module './utils'

Cause: Bun resolves modules according to Node.js resolution rules. Missing file extension or incorrect path casing on case-sensitive filesystems.

Solution:

  1. Ensure the import path matches the filename exactly (case-sensitive on Linux).
  2. Add the file extension if using .ts files that import other .ts files: import { foo } from './utils.ts' — Bun supports explicit .ts extensions in imports.
  3. Check that tsconfig.json paths aliases are defined correctly if using aliased imports.

Prevention: Use an editor with TypeScript support (VS Code + the Bun extension) to catch missing module errors at edit time.


Error: EPERM: operation not permitted during bun install

Cause: Tracked in issue #29398. Occurs when patching packages on Windows or when the --backend uses filesystem operations that require elevated permissions.

Solution:

  1. On Windows, try switching the install backend: bun install --backend copyfile
  2. Ensure no antivirus or security software is holding file locks on node_modules.
  3. Delete node_modules and bun.lockb, then retry: rm -rf node_modules bun.lockb && bun install

Error: EADDRINUSE: Address already in use in VS Code

Cause: Tracked in issue #16242. The integrated VS Code terminal can hold open a previous Bun process's port, especially when stopping a dev server with Ctrl+C.

Solution:

  1. Kill the process holding the port: lsof -ti:3000 | xargs kill -9 (macOS/Linux) or use Task Manager on Windows.
  2. Use a different port: bun run --port 3001 server.ts
  3. Switch to an external terminal for running Bun dev servers.

Error: Segmentation fault when using FFI on Linux x64

Cause: Tracked in issue #29346. Certain FFI (Foreign Function Interface) call patterns cause a segfault on Linux x64 in Bun 1.x.

Solution:

  1. Check if your FFI signature matches the native library's actual ABI exactly (including calling convention).
  2. Ensure all pointer types are declared correctly in FFIType.
  3. If the segfault is reproducible and your FFI code looks correct, file a bug on the Bun GitHub tracker referencing issue #29346.

Error: Segmentation fault in HTTP server

Cause: Tracked in issue #15281. Certain request patterns or server configurations can cause a server crash in specific Bun versions.

Solution:

  1. Upgrade Bun: bun upgrade
  2. Wrap your fetch handler in a try/catch to prevent unhandled errors from crashing the process.
  3. Enable the --watch flag during development so the process auto-restarts.

Error: Codesign failure with bun build --compile on macOS

Cause: Tracked in issue #29361. A regression introduced codesigning issues for compiled binaries on macOS.

Solution:

  1. Upgrade Bun: bun upgrade
  2. Manually re-sign the binary: codesign --force --deep --sign - ./my-binary
  3. If distributing, use a proper Apple Developer ID signature.

Error: bun install not linking bin from a package (e.g., @anthropic-ai/claude-code)

Cause: Tracked in issue #29399. Certain packages with bin fields in their package.json are not correctly linked into .bin/ after install.

Solution:

  1. Run bun pm bin to confirm the bin directory location.
  2. Delete node_modules and reinstall: rm -rf node_modules && bun install
  3. Add --trust flag for packages with lifecycle scripts: bun add --trust @anthropic-ai/claude-code
  4. As a workaround, run the package directly: bunx @anthropic-ai/claude-code

Error: Drive root path failure on Windows

Cause: Tracked in issue #29273. Paths beginning at a Windows drive root (e.g., C:\) may not resolve correctly in certain Bun APIs.

Solution:

  1. Use forward slashes: C:/Users/... instead of C:\Users\...
  2. Use relative paths where possible.
  3. Upgrade Bun: bun upgrade

Error: File.type returns wrong charset

Cause: Tracked in issue #29257. BunFile.type (MIME type) may include an incorrect or missing charset component for certain file types.

Solution:

  1. Explicitly set the Content-Type header in HTTP responses rather than relying on Bun.file().type.
  2. Use new Response(body, { headers: { "Content-Type": "text/html; charset=utf-8" } }).

Memory Leak with MongoDB

Cause: Tracked in issue #12117. A memory leak occurs when using the mongodb npm package with Bun under sustained connection load.

Solution:

  1. Implement connection pooling and explicitly close connections when done.
  2. Monitor memory with process.memoryUsage() to confirm the leak.
  3. Consider using a managed connection pool library (e.g., mongoose with autoIndex: false to reduce connection churn).
  4. Check for a newer version of mongodb that may have patched the underlying issue.
  5. File a reproduction on the Bun GitHub tracker referencing issue #12117 if the leak persists on current versions.

100% CPU Usage When Debugging

Cause: Tracked in issue #21654. The Bun debugger (--inspect) can spin at 100% CPU in certain debug session configurations.

Solution:

  1. Use --inspect-wait instead of --inspect to ensure the debugger attaches before execution begins.
  2. Set breakpoints before resuming execution.
  3. Disconnect and reconnect the debugger if CPU usage spikes.
  4. Upgrade Bun: bun upgrade

Lockfile Conflicts (bun.lockb vs bun.lock)

Cause: Projects switching between binary (bun.lockb) and text (bun.lock) lockfile formats, or teams using different Bun versions, can encounter conflicts.

Solution:

  1. Agree on one format. Add save-text-lockfile = true to bunfig.toml to use the text format for easier code review.
  2. Delete the old lockfile and regenerate: rm bun.lockb && bun install --save-text-lockfile
  3. In CI, always use --frozen-lockfile to fail fast on drift.

bunfig.toml Parse Errors

Cause: TOML syntax errors or incorrect types in bunfig.toml.

Solution:

  1. Validate your TOML at tomllib.dev or with python3 -c "import tomllib; tomllib.loads(open('bunfig.toml').read())".
  2. Common mistake: using "true" (string) instead of true (boolean).
  3. Check for stray commas — TOML (unlike JSON) does not allow trailing commas.

Example of a correct section:

[install]
production = false        # boolean, not "false"
frozen-lockfile = false   # boolean

Error: TypeScript path aliases not resolving at runtime

Cause: tsconfig.json path aliases are not configured in bunfig.toml or in a way Bun recognizes.

Solution: Bun reads compilerOptions.paths from tsconfig.json automatically. Ensure your tsconfig.json is at the project root and the baseUrl is set:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@utils/*": ["src/utils/*"],
      "@types/*": ["src/types/*"]
    }
  }
}

No additional Bun configuration is needed.


Error: bun: command not found after install

Cause: The Bun binary directory (~/.bun/bin) is not in your PATH.

Solution:

# Add to ~/.bashrc or ~/.zshrc:
export BUN_INSTALL="$HOME/.bun"
export PATH="$BUN_INSTALL/bin:$PATH"

# Then reload:
source ~/.bashrc

7. FAQ

How do I switch from npm to Bun? Drop-in replacement: run bun install instead of npm install, bun run <script> instead of npm run <script>, and bun add <pkg> instead of npm install <pkg>. Your package.json and existing node_modules structure remain unchanged. Bun reads package.json the same way npm does.

How do I switch from yarn or pnpm to Bun? The same principle applies. Bun understands yarn.lock and pnpm-lock.yaml and can migrate from them: running bun install will replace either lockfile with bun.lockb. Delete the old lockfile after migrating to avoid confusion.

Is Bun compatible with Node.js? Bun implements the Node.js API surface including fs, path, http, https, crypto, stream, buffer, child_process, worker_threads, os, net, and more. Most Node.js code runs without modification. Gaps and known incompatibilities are tracked at bun.sh/nodejs. Native addons (.node files compiled for Node.js) may not work.

Does Bun work on Windows? Yes. Bun has full Windows support for x64 and arm64. The built-in shell (Bun.$) handles cross-platform scripting so you don't need WSL for most tasks. Install via scoop install bun or the npm package (npm install -g bun).

How do I use Bun with Docker? Use the official oven/bun images. A minimal production Dockerfile:

FROM oven/bun:1.3.13-alpine AS builder
WORKDIR /app
COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile --production
COPY src/ ./src/

FROM oven/bun:1.3.13-distroless
WORKDIR /app
COPY --from=builder /app .
CMD ["bun", "run", "src/server.ts"]

What file types does Bun support natively? .js, .jsx, .ts, .tsx, .mjs, .cjs, .mts, .cts — all executed natively without plugins. Bun also handles .json (imported as objects), .toml (imported as objects), .txt (imported as strings), and .html (in the experimental Bun Bake bundler).

How do I debug with VS Code? Start Bun with the inspector enabled:

bun run --inspect-wait src/server.ts

In VS Code, open the Run and Debug panel, create a launch.json with type "bun", and attach to ws://localhost:6499. The Bun VS Code extension (available in the marketplace) automates this configuration.

Warning

Running Bun with --inspect may cause 100% CPU usage in some versions (issue #21654). Use --inspect-wait and set breakpoints before resuming to mitigate this.

How do I generate a standalone executable? Use bun build --compile:

bun build src/cli.ts --compile --outfile my-tool

The resulting binary includes the Bun runtime and your code. It runs on any machine with the same OS and architecture — no Bun, Node.js, or npm required.

What's the difference between --watch and --hot? --watch restarts the process cleanly on file changes (new process, fresh state). --hot hot-patches changed modules into the running process (same process, preserved state). Use --watch for CLIs and scripts; use --hot for HTTP servers where you don't want to drop connections.

How do I use workspaces in a monorepo? Add a workspaces field to your root package.json:

{ "workspaces": ["packages/*", "apps/*"] }

Then bun install at the root installs all workspace dependencies. Use bun run --filter "packages/*" build to run scripts across workspaces.

Can Bun replace webpack or esbuild? For most use cases, yes. bun build handles tree-shaking, code splitting, source maps, minification, and targets (browser, Node.js, Bun). It reads tsconfig.json path aliases. For highly customized webpack configs with obscure loaders, you may need to migrate plugin logic, but Bun's build API supports custom plugins written in TypeScript.

Does Bun support ESM and CommonJS? Both. You can import and require() in the same file. Bun resolves .mjs as ESM, .cjs as CommonJS, and .js based on the "type" field in package.json. ESM packages that conditionally export based on "exports" are fully supported.

How do I use Bun with an existing Node.js project?

  1. Install Bun: curl -fsSL https://bun.sh/install | bash
  2. In your project directory: bun install (replaces npm install)
  3. Replace node src/server.js with bun src/server.js in your scripts
  4. Test thoroughly — most projects work without changes

How do I configure test coverage thresholds? Bun does not yet enforce minimum coverage thresholds via CLI flags (use a separate tool like nyc for enforcement). However, you can fail the CI pipeline by checking the text output:

bun test --coverage 2>&1 | tee coverage.txt
# Parse coverage.txt for your threshold check

Configure reporters and output directory in bunfig.toml:

[test]
coverage = true
coverageReporter = ["text", "lcov"]
coverageDir = "coverage"

What is bunfig.toml? bunfig.toml is Bun's configuration file, analogous to .npmrc or jest.config.ts. It configures the package manager ([install]), script runner ([run]), test runner ([test]), and more. Place it at the project root or at ~/.bunfig.toml for global defaults. Bun merges both files, with the project-level file taking precedence.

What's the difference between bun run and bun exec? bun run <file> runs a JavaScript/TypeScript file using Bun's runtime, or a script name from package.json. bun exec <script.sh> executes a shell script file using Bun's built-in cross-platform shell — equivalent to running bash script.sh but without requiring bash on the system, making it useful for cross-platform scripts on Windows.

How do I use the built-in Redis client? Set REDIS_URL in your environment and use Bun.redis:

const client = new Bun.RedisClient("redis://localhost:6379");
await client.set("key", "value");
const val = await client.get("key");
// "value"
await client.disconnect();

// Or use the default client (reads REDIS_URL)
await Bun.redis.set("count", 1);

How do I use the built-in S3 client?

const s3 = new Bun.S3Client({
  region: "us-east-1",
  accessKeyId: Bun.env.AWS_ACCESS_KEY_ID,
  secretAccessKey: Bun.env.AWS_SECRET_ACCESS_KEY,
  bucket: "my-bucket",
});

// Upload
await s3.write("uploads/image.png", imageBuffer);

// Download
const file = s3.file("uploads/image.png");
const data = await file.arrayBuffer();

// Or use the default client (reads AWS_* env vars)
const file = Bun.s3("s3://my-bucket/key.txt");

How do I transpile code programmatically?

const transpiler = new Bun.Transpiler({
  loader: "ts",
  target: "browser",
});

const result = await transpiler.transform(`
  const greet = (name: string) => \`Hello, \${name}!\`;
  export { greet };
`);

console.log(result);
// const greet = (name) => `Hello, ${name}!`;
// export { greet };