Note
This guide targets Bun v1.3.13 (commit
9ed6e89, 2026-05-07). It covers architecture internals, build system, configuration reference, deployment patterns, and operational concerns. For end-user installation and everyday usage, see the companion User Guide.
1. Architecture Overview
Bun is an all-in-one JavaScript/TypeScript runtime written primarily in Zig and C++, with TypeScript used for build tooling and type definitions. Unlike Node.js and Deno, Bun embeds JavaScriptCore (WebKit's JS engine) rather than V8. This choice is deliberate: JSC starts faster and uses less memory for typical server workloads, while Zig enables fine-grained control over memory layout and system calls without a GC overhead in the runtime host.
The architecture is monolithic by design — the runtime, bundler, transpiler, package manager, test runner, and shell all live in the same binary and share internal data structures. This avoids serialization overhead between tools and lets the bundler directly feed the runtime without writing intermediate files.
1.1 Component Inventory
| Component | Path | Role |
|---|---|---|
| Runtime core | src/ (117 files, 3.2M chars) |
Main Zig runtime, event loop, system calls |
| JavaScript engine bindings | src/bun.js/ |
JavaScriptCore FFI layer |
| HTTP server/client | src/http/ (29 files) |
HTTP/1.1, HTTP/2, WebSocket, TLS |
| Package manager | src/install/ (29 files, 1M chars) |
npm-compatible package installer |
| Bundler | src/bundler/ (16 files, 576K chars) |
JavaScript/TypeScript bundler |
| Transpiler | src/ast/ (46 files, 1.6M chars), src/js_lexer/ |
JS/TS/JSX parser and transpiler |
| CLI | src/cli/ (44 files, 1.5M chars) |
Command-line interface, argument parsing |
| Shell | src/shell/ (15 files, 430K chars) |
Built-in shell for Bun.$ |
| Module resolver | src/resolver/ (6 files, 401K chars) |
Module resolution (ESM, CJS, package.json exports) |
| CSS parser | src/css/ (17 files, 817K chars) |
CSS parsing and bundling |
| SQL clients | src/sql/, src/sql/postgres/protocol/, src/sql/mysql/protocol/ |
Built-in PostgreSQL and MySQL drivers |
| S3 client | src/s3/ (10 files, 184K chars) |
Built-in S3-compatible storage client |
| Valkey/Redis | src/valkey/ (7 files, 219K chars) |
Built-in Redis/Valkey client |
| Markdown parser | src/md/ (16 files, 458K chars) |
GFM markdown parser with HTML/ANSI/React output |
| Codegen | src/codegen/ (23 files, 451K chars) |
Build-time code generation (binding gen, module bundling) |
| Threading | src/threading/ (8 files, 91K chars) |
Thread pool, concurrent I/O |
| Async I/O | src/async/ (3 files, 60K chars), src/io/ (9 files, 165K chars) |
Event loop, async primitives |
| Source maps | src/sourcemap/ (9 files, 174K chars) |
Source map generation and consumption |
| Glob | src/glob/ (2 files, 100K chars) |
Fast glob pattern matching |
| Semver | src/semver/ (7 files, 102K chars) |
Semantic versioning parser |
| Memory allocators | src/allocators/ (10 files, 64K chars) |
Custom memory allocators |
| Safety | src/safety/ (4 files, 18K chars) |
Safety checks and debug assertions |
| File watcher | src/watcher/ (4 files, 34K chars) |
File system watcher (--watch/--hot) |
| Node.js compat | src/node-fallbacks/ (25 files, 170K chars), src/js/internal/ (21 files) |
Node.js API polyfills |
| NAPI | src/napi/ (5 files, 146K chars) |
Node-API native addon support |
| Bake (SSR) | src/bake/ (21 files, 402K chars) |
Server-side rendering framework (experimental) |
| Error reporting | src/errno/ (3 files, 58K chars) |
System error handling |
1.2 Engine: JavaScriptCore vs. V8
Replacing V8 with JSC touches every integration point:
- GC: JSC uses a concurrent, generational GC with a separate MarkedBlock allocator. Bun exposes
--expose-gcto makegc()callable from JS for testing purposes. - JIT tiers: JSC has four tiers — LLInt (interpreter), Baseline JIT, DFG JIT, and FTL JIT (LLVM-backed). Tier selection is automatic.
- Bytecode:
bun build --bytecodeemits JSC bytecode that JSC can execute without reparsing — useful for reducing cold-start overhead on large bundles. - V8 compatibility: A V8 API compatibility shim exists for native addons (
src/napi/), but coverage is partial. Addons relying on V8 internals may not work.
1.3 Package Inventory
| Package | Purpose |
|---|---|
packages/bun-types (24 files, 843K chars) |
TypeScript type definitions for Bun APIs |
packages/bun-release |
Release automation and npm publishing |
packages/bun-vscode |
VS Code extension (debugger, run configurations) |
packages/bun-lambda |
AWS Lambda runtime layer |
packages/bun-native-plugin-rs |
Rust SDK for native bundler plugins |
packages/bun-debug-adapter-protocol |
DAP implementation for debugger |
packages/bun-inspector-protocol |
Chrome DevTools protocol |
packages/bun-usockets / packages/bun-uws |
µWebSockets (C networking layer) |
packages/bun-wasm |
WebAssembly build of Bun transpiler |
packages/bun-error |
Error formatting and display |
packages/bun-plugin-svelte |
Svelte plugin for bundler |
packages/bun-plugin-yaml |
YAML loader plugin |
packages/bun-build-mdx-rs |
MDX compiler (Rust, via NAPI) |
1.4 Technology Stack
| Language | Usage |
|---|---|
| Zig | Core runtime, event loop, parser, package manager, resolver |
| C++ | JavaScriptCore bindings, µWebSockets |
| TypeScript | Codegen scripts, type definitions, test suite |
| Rust | Native bundler plugins (bun-native-plugin-rs), MDX compiler |
| JavaScript | Node.js fallbacks, internal builtins |
2. System Requirements
2.1 Runtime Requirements
| Platform | Minimum Version | Architecture |
|---|---|---|
| macOS | 12 (Monterey) | x64, ARM64 (Apple Silicon) |
| Linux | Kernel 5.1+ | x64, ARM64 |
| Windows | Windows 10 (Build 17063+) | x64 |
Important
On Linux, Bun requires a kernel with
io_uringsupport (5.1+). Some container environments (particularly older Docker hosts or restrictive seccomp profiles) may blockio_uringsyscalls. UseBUNNOI_ORING=1environment variable or verify your container security profile permitsio_uringif you see unexpected I/O failures.
glibc requirement (Linux x64): glibc 2.17+. For older systems, use the musl-based binary.
Memory: No hard minimum; practical production minimum is 128 MB for small services. Use --smol to reduce heap size at the cost of throughput.
2.2 Building from Source
| Dependency | Notes |
|---|---|
| Zig compiler | Vendored at vendor/zig/ — do not use a system Zig |
| CMake | Build system driver |
| Clang / C++ compiler | Required for JSC and µWebSockets |
| Node.js | Required for codegen scripts (src/codegen/) |
| Bun (bootstrap) | Needed to run package.json build scripts |
Build profiles available:
| Profile | Flag | Use Case |
|---|---|---|
debug |
bun run build:debug |
Full debug symbols, ASAN enabled |
release |
bun run build:release |
Optimized production build |
ci-release |
bun run build:ci |
CI-specific release build |
release-assertions |
— | Release with runtime assertions |
release-asan |
— | Release with address sanitizer |
debug-no-asan |
— | Debug without ASAN overhead |
# Clone and bootstrap
git clone https://github.com/oven-sh/bun
cd bun
# Debug build (faster compile, slower runtime)
bun run build:debug
# Release build (slower compile, production-optimized)
bun run build:release
# CI release (used in GitHub Actions)
bun run build:ci
Note
Always use the vendored Zig at
vendor/zig/. The Zig language is not yet API-stable; the vendored version is pinned to the exact revision Bun's source was written against. Using a mismatched Zig version will produce compile errors.
3. Configuration Reference
3.1 bunfig.toml — Complete Reference
bunfig.toml is Bun's project-level configuration file. Bun reads it from the project root. A global config at ~/.bunfig.toml (or $XDG_CONFIG_HOME/.bunfig.toml) applies as a fallback for all projects.
Tip
Project-level
bunfig.tomlsettings take precedence over the global config. You can use--config <path>to specify an alternate config file path for any command.
[install] — Package Manager
[install]
# Default registry (can be a string URL or a table with auth)
registry = "https://registry.npmjs.org"
# Equivalent per-scope registry override
[install.scopes]
"@myorg" = { token = "abc123", url = "https://npm.myorg.com" }
"@privateorg" = { password = "pw", username = "user", url = "https://npm.example.com" }
[install]
# Save packages to package.json by default (true/false/"development"/"production"/"optional")
save = true
# Auto-install missing dependencies when running scripts
auto = "auto" # auto | fallback | force | disable
# Cache directory (default: platform-specific)
cache = "~/.bun/install/cache"
# Disable integrity verification (not recommended)
verify = true
# Run lifecycle scripts from these packages only
trustedDependencies = ["esbuild", "sharp"]
# Disable running lifecycle scripts entirely
ignoreScripts = false
# Number of concurrent install scripts (default: 5)
concurrentScripts = 5
# Number of concurrent network requests (default: 48)
networkConcurrency = 48
# Linker mode: isolated (default) or hoisted (node_modules-style)
linker = "isolated" # isolated | hoisted
# Save a text-format lockfile alongside the binary lockfile
saveTextLockfile = false
# Omit dependency groups
omit = [] # ["dev", "optional", "peer"]
# Hardlink backend (macOS: clonefile is default)
backend = "hardlink" # clonefile | hardlink | symlink | copyfile
[run] — Script Runner
[run]
# Default shell for bun run (bun or system)
shell = "bun" # bun | system
# Suppress script output
silent = false
# Elide output lines beyond this count (0 = unlimited)
elidedLines = 0
[test] — Test Runner
[test]
# Root directory for test discovery
root = "."
# Timeout per test in milliseconds
timeout = 5000
# Retry count on failure
retries = 0
# Re-run each test N times
reruns = 0
# Coverage collection
coverage = false
# Coverage reporters (text, lcov, clover)
coverageReporter = ["text"]
# Coverage output directory
coverageDir = "coverage"
# Coverage threshold thresholds
coverageThreshold = { lines = 0, functions = 0, statements = 0 }
# Bail after N failures (0 = never)
bail = 0
# Randomize test order
randomize = false
# Seed for randomization
seed = 0
[bundle] / [build] — Bundler
[bundle]
# Default output directory
outdir = "dist"
# Target platform
target = "browser" # browser | bun | node
# Source maps
sourcemap = "none" # none | inline | linked | external
# Minification
minify = false
minifyWhitespace = false
minifySyntax = false
minifyIdentifiers = false
# Code splitting
splitting = false
# Public path for assets
publicPath = "/"
# External packages (array of patterns)
external = ["some-package"]
# Packages treatment
packages = "bundle" # bundle | external
# Emit DCE annotations
emitDceAnnotations = false
# Bytecode compilation
bytecode = false
[debug] — Inspector
[debug]
# Inspector host:port
inspector = "localhost:6499"
[telemetry]
[telemetry]
# Disable telemetry entirely
disable = true
3.2 Environment Variables
Important
Environment variables are read at startup. Changes require a process restart. Use
--env-file <path>to load from a dotenv file, or--no-env-fileto suppress automatic.envloading.
| Variable | Purpose | Source |
|---|---|---|
NODE_ENV |
Runtime environment (development, production, test) |
Standard |
BUN_INSPECT_NOTIFY |
Notification URL for debugger attach events | src/js/internal/debugger.ts |
TZ |
Timezone override (IANA tz database name) | Runtime |
REDIS_URL / VALKEY_URL |
Default connection URL for new Redis() / new Valkey() |
packages/bun-types/redis.d.ts |
DATABASE_URL |
Default SQL connection URL | SQL adapter |
PGHOST |
PostgreSQL host | SQL adapter |
PGUSER |
PostgreSQL username | SQL adapter |
PGPASSWORD |
PostgreSQL password | SQL adapter |
POSTGRES_URL |
Full PostgreSQL connection URL | SQL adapter |
PGURL |
Alias for POSTGRES_URL |
SQL adapter |
MYSQL_URL |
Full MySQL connection URL | SQL adapter |
MYSQL_HOST |
MySQL host | SQL adapter |
MYSQL_PORT |
MySQL port | SQL adapter |
MYSQL_USER |
MySQL username | SQL adapter |
MYSQL_PASSWORD |
MySQL password | SQL adapter |
MYSQL_DATABASE |
MySQL database name | SQL adapter |
AWS_SECRET_ACCESS_KEY |
AWS/S3 secret key for S3 client | S3 client |
3.3 CLI Flags — Complete Reference
Global Flags (all commands)
| Flag | Description |
|---|---|
--env-file <path> |
Load environment variables from a dotenv file |
--no-env-file |
Disable automatic .env file loading |
--cwd <path> |
Set working directory before executing |
-c, --config <path> |
Path to bunfig.toml (default: project root) |
-h, --help |
Show help text |
Runtime Flags (bun run, bun <file>)
Watch / Hot Reload
| Flag | Description |
|---|---|
--watch |
Restart process on file changes |
--hot |
Hot-reload modules without restarting the process |
--no-clear-screen |
Disable screen clear on reload |
Memory & Performance
| Flag | Description |
|---|---|
--smol |
Use a smaller JSC heap (trades throughput for memory) |
--expose-gc |
Expose gc() function to JavaScript |
--zero-fill-buffers |
Zero-fill Buffer allocations (security) |
Module Loading
| Flag | Description |
|---|---|
-r, --preload <module> |
Execute a module before the entry point |
--require <module> |
Alias for --preload (Node.js compat) |
--import <module> |
ESM-style preload |
--no-install |
Do not auto-install missing packages |
--install <mode> |
Auto-install behavior: auto, fallback, force |
-i |
Shorthand for --install=force |
Networking
| Flag | Description |
|---|---|
--port <n> |
Default port (sets process.env.PORT) |
--fetch-preconnect <url> |
Warm up a TCP connection before the script starts |
--max-http-header-size <bytes> |
Maximum HTTP header size |
--dns-result-order <order> |
DNS result order: verbatim, ipv4first, ipv6first |
--redis-preconnect |
Pre-connect to REDIS_URL/VALKEY_URL at startup |
--sql-preconnect |
Pre-connect to DATABASE_URL at startup |
TLS / CA
| Flag | Description |
|---|---|
--use-system-ca |
Use the OS certificate store |
--use-openssl-ca |
Use OpenSSL's certificate store |
--use-bundled-ca |
Use Bun's bundled CA (default) |
Debugging & Profiling
| Flag | Description |
|---|---|
--inspect [host:port] |
Enable WebSocket debugger (default localhost:6499) |
--inspect-wait [host:port] |
Pause execution until debugger attaches |
--inspect-brk [host:port] |
Break on first line of script |
--cpu-prof |
Write a CPU profile on exit |
--cpu-prof-name <name> |
Profile file name |
--cpu-prof-dir <dir> |
Profile output directory |
--cpu-prof-md |
Write CPU profile as Markdown (LLM-friendly) |
--cpu-prof-interval <µs> |
Sampling interval in microseconds (default: 1000) |
--heap-prof |
Write a heap profile on exit |
--heap-prof-name <name> |
Heap profile file name |
--heap-prof-dir <dir> |
Heap profile output directory |
--heap-prof-md |
Write heap profile as Markdown |
Error Handling
| Flag | Description |
|---|---|
--unhandled-rejections <mode> |
strict, throw, warn, none, warn-with-error-code |
--no-deprecation |
Suppress deprecation warnings |
--throw-deprecation |
Throw on deprecated API use |
Process
| Flag | Description |
|---|---|
-e, --eval <code> |
Evaluate a string of JavaScript |
-p, --print <code> |
Evaluate and print result |
--if-present |
Exit 0 if entry point does not exist |
--title <string> |
Set process title |
--user-agent <string> |
Override HTTP User-Agent header |
--no-addons |
Disable native addon loading |
--conditions <conditions> |
Conditional exports conditions (comma-separated) |
--console-depth <n> |
Max depth for console.log object inspection |
--cron-title <title> |
Title for cron-style scheduling |
--cron-period <period> |
Period for cron-style scheduling |
Transpiler Flags
| Flag | Description |
|---|---|
--main-fields <fields> |
Override package.json main fields order |
--preserve-symlinks |
Preserve symlinks during resolution |
--preserve-symlinks-main |
Preserve symlinks for the main entry point |
--extension-order <exts> |
Extension resolution order (comma-separated) |
--tsconfig-override <path> |
Use alternate tsconfig.json |
-d, --define <key=value> |
Replace an identifier with a literal value |
--drop <name> |
Remove calls to named function |
--feature <name> |
Enable/disable language feature |
-l, --loader <ext:loader> |
Assign a loader to a file extension |
--no-macros |
Disable Bun macro evaluation |
--jsx-factory <expr> |
JSX factory function |
--jsx-fragment <expr> |
JSX fragment factory |
--jsx-import-source <pkg> |
JSX import source package |
--jsx-runtime <mode> |
automatic or classic |
--jsx-side-effects |
Prevent JSX from being tree-shaken |
--ignore-dce-annotations |
Ignore @__PURE__ and similar annotations |
bun run Script Flags
| Flag | Description |
|---|---|
--silent |
Suppress script output |
--elide-lines <n> |
Truncate long output |
-F, --filter <pattern> |
Run scripts in matching workspaces |
-b, --bun |
Force using Bun's runtime even for node shebangs |
--shell <bun|system> |
Shell to use for script execution |
--workspaces |
Run across all workspaces |
--parallel |
Run matching scripts in parallel |
--sequential |
Run matching scripts sequentially (default) |
--no-exit-on-error |
Continue running remaining scripts after failure |
-v, --version |
Print Bun version |
--revision |
Print Bun revision (git commit) |
bun build Flags
| Flag | Description |
|---|---|
--production |
Production mode (sets NODE_ENV=production, enables minification) |
--compile |
Produce a standalone executable |
--compile-exec-argv <args> |
Extra argv for embedded binary |
--compile-autoload-dotenv / --no-compile-autoload-dotenv |
Auto-load .env in compiled binary |
--compile-autoload-bunfig / --no-compile-autoload-bunfig |
Auto-load bunfig.toml in compiled binary |
--compile-autoload-tsconfig / --no-compile-autoload-tsconfig |
Auto-load tsconfig.json in compiled binary |
--compile-autoload-package-json / --no-compile-autoload-package-json |
Auto-load package.json in compiled binary |
--compile-executable-path <path> |
Path to a custom Bun binary to embed |
--bytecode |
Emit JSC bytecode |
--watch |
Rebuild on file changes |
--no-clear-screen |
Disable screen clear on rebuild |
--target <platform> |
Target platform: browser, bun, node |
--outdir <dir> |
Output directory |
--outfile <file> |
Output file (single-entry builds) |
--metafile |
Write build metadata JSON |
--metafile-md |
Write build metadata as Markdown |
--sourcemap <mode> |
linked, inline, external, none |
--banner <text> |
Prepend text to output files |
--footer <text> |
Append text to output files |
--format <fmt> |
Output format: esm, cjs, iife |
--root <dir> |
Entry-point root directory |
--splitting |
Enable code splitting |
--public-path <path> |
Public base path for asset URLs |
-e, --external <pkg> |
Mark package as external |
--allow-unresolved |
Allow unresolved imports (no error) |
--reject-unresolved |
Error on unresolved imports |
--packages <mode> |
external or bundle |
--entry-naming <pattern> |
Entry chunk file naming pattern |
--chunk-naming <pattern> |
Non-entry chunk file naming pattern |
--asset-naming <pattern> |
Asset file naming pattern |
--react-fast-refresh |
Enable React Fast Refresh |
--no-bundle |
Transpile only, no bundling |
--emit-dce-annotations |
Emit @__PURE__ annotations in output |
--minify |
Enable all minification |
--minify-syntax |
Minify syntax (fold constants, simplify branches) |
--minify-whitespace |
Remove whitespace |
--minify-identifiers |
Shorten identifier names |
--keep-names |
Preserve function/class .name properties |
--css-chunking |
Enable CSS code splitting |
--conditions <c> |
Conditional package export conditions |
--app |
Enable Bake application mode (experimental) |
--server-components |
Enable server component support (experimental) |
--env <mode> |
Environment variable inlining: inline, prefix*, disable |
--windows-hide-console |
Hide the console window on Windows |
--windows-icon <path> |
Path to .ico for compiled executable |
--windows-title <str> |
Executable title metadata |
--windows-publisher <str> |
Publisher metadata |
--windows-version <str> |
Version metadata |
--windows-description <str> |
Description metadata |
--windows-copyright <str> |
Copyright metadata |
bun test Flags
| Flag | Description |
|---|---|
--timeout <ms> |
Per-test timeout in milliseconds |
-u, --update-snapshots |
Update stored snapshots |
--rerun-each <n> |
Run each test N times |
--retry <n> |
Retry count on test failure |
--todo |
Run test.todo() tests |
--only |
Run only test.only() tests |
--pass-with-no-tests |
Exit 0 when no tests found |
--concurrent |
Run test files concurrently |
--randomize |
Randomize test execution order |
--seed <n> |
Seed for --randomize |
--coverage |
Collect code coverage |
--coverage-reporter <reporter> |
Coverage format: text, lcov, clover |
--coverage-dir <dir> |
Coverage output directory |
--bail <n> |
Stop after N failures |
-t, --test-name-pattern <pattern> |
Filter tests by name (alias: --grep) |
--reporter <fmt> |
Output reporter: junit, dots |
--reporter-outfile <path> |
Reporter output file |
--dots |
Shorthand for --reporter=dots |
--only-failures |
Re-run only failing tests |
--max-concurrency <n> |
Maximum concurrent test files |
--path-ignore-patterns <patterns> |
Glob patterns to exclude |
--changed |
Run only tests for changed files (git-aware) |
bun install / bun add / bun remove Flags
| Flag | Description |
|---|---|
-c, --config <path> |
Path to bunfig.toml |
-y, --yarn |
Write a Yarn v1-compatible yarn.lock |
-p, --production |
Install production dependencies only |
--no-save |
Do not update package.json |
--save |
Always update package.json (default for add) |
--ca <cert> |
Custom CA certificate (PEM string) |
--cafile <path> |
Path to CA certificate bundle |
--dry-run |
Show what would be installed, don't write |
--frozen-lockfile |
Error if bun.lock would change |
-f, --force |
Force re-install (ignore cache) |
--cache-dir <dir> |
Package cache directory |
--no-cache |
Disable the cache |
--silent |
Suppress all output |
--quiet |
Suppress non-essential output |
--verbose |
Verbose output |
--no-progress |
Disable progress indicators |
--no-summary |
Suppress install summary |
--no-verify |
Skip integrity verification (not recommended) |
--ignore-scripts |
Do not run lifecycle scripts |
--trust |
Add package to trustedDependencies |
-g, --global |
Install globally |
--cwd <path> |
Working directory |
--backend <mode> |
Install backend: clonefile, hardlink, symlink, copyfile |
--registry <url> |
Override registry URL |
--concurrent-scripts <n> |
Concurrent lifecycle scripts (default: 5) |
--network-concurrency <n> |
Concurrent network requests (default: 48) |
--save-text-lockfile |
Save human-readable bun.lock text file |
--omit <group> |
Omit dependency groups: dev, optional, peer |
--lockfile-only |
Only update lockfile, do not install |
--linker <mode> |
isolated or hoisted |
-d, --dev |
Save to devDependencies |
--optional |
Save to optionalDependencies |
--peer |
Save to peerDependencies |
-E, --exact |
Save exact version (no range) |
-a, --analyze |
Analyze package for security information |
--only-missing |
Install only packages not yet present |
--filter <pattern> |
Limit to matching workspaces |
--latest |
Install the latest version |
-i, --interactive |
Prompt for confirmation |
-r, --recursive |
Recurse into workspaces |
4. Deployment & Scaling
4.1 Docker
Bun publishes official Docker images on Docker Hub under oven/bun.
| Image | Base | Use Case |
|---|---|---|
oven/bun:latest |
Debian | General purpose |
oven/bun:alpine |
Alpine Linux | Minimal image size |
oven/bun:debian-slim |
Debian slim | Balance of size and compatibility |
oven/bun:distroless |
Distroless | Security-hardened, no shell |
Tip
Use the
distrolessvariant in production for a minimal attack surface. It contains only the Bun binary and its dependencies — no shell, no package manager, no extra utilities. Debug viabun --inspectover Tailscale or a sidecar.
Multi-stage production Dockerfile:
# Build stage — install dependencies with dev tools available
FROM oven/bun:latest AS deps
WORKDIR /app
COPY package.json bun.lock* ./
RUN bun install --frozen-lockfile
# Production stage — copy only what's needed
FROM oven/bun:distroless AS runner
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
EXPOSE 3000
USER nonroot
CMD ["bun", "run", "start"]
Build a standalone executable inside Docker:
FROM oven/bun:latest AS build
WORKDIR /app
COPY . .
RUN bun build --compile --minify --target=bun ./src/index.ts --outfile=server
FROM debian:bookworm-slim AS runner
WORKDIR /app
COPY --from=build /app/server .
EXPOSE 3000
CMD ["./server"]
4.2 Standalone Executables
bun build --compile embeds the entry point and all dependencies into a single self-contained binary. No Bun or Node.js installation is required on the target machine.
# Basic compile
bun build --compile ./src/index.ts --outfile=myapp
# With minification and production mode
bun build --compile --minify --production ./src/index.ts --outfile=myapp
# Cross-compilation (specify target binary)
bun build --compile --target=bun --compile-executable-path=/path/to/bun-linux-x64 \
./src/index.ts --outfile=myapp-linux
# Windows-specific metadata
bun build --compile \
--windows-icon=assets/icon.ico \
--windows-title="My App" \
--windows-version="1.0.0.0" \
--windows-description="My application" \
./src/index.ts --outfile=myapp.exe
Dotenv / config autoloading in compiled binaries:
By default, compiled binaries autoload .env, bunfig.toml, tsconfig.json, and package.json from the working directory at runtime. Disable selectively:
bun build --compile \
--no-compile-autoload-dotenv \
--no-compile-autoload-bunfig \
./src/index.ts --outfile=myapp
4.3 AWS Lambda
packages/bun-lambda provides a Lambda runtime layer that runs Bun-based handlers without the Lambda Node.js runtime.
# Install
bun add bun-lambda
# Handler structure
export const handler = async (event: AWSLambdaEvent) => {
return { statusCode: 200, body: "Hello" };
};
Publish the layer via the scripts in packages/bun-lambda. The layer ARN is published in the package's README and updated per Bun release.
4.4 Workspaces and Monorepos
Bun supports npm-compatible workspaces defined in the root package.json:
{
"workspaces": ["packages/*", "apps/*"]
}
Run scripts across workspaces:
# Run "build" in all workspaces in parallel
bun run --filter='*' build --parallel
# Run in matching workspaces only
bun run --filter='@myorg/*' test
# Run sequentially
bun run --filter='*' build --sequential
4.5 Process Management
Bun does not ship a built-in process manager (no equivalent to PM2). For production deployments:
- systemd (Linux): create a unit file targeting the Bun binary or compiled executable
- Docker with restart policies:
--restart=unless-stopped - Kubernetes: treat Bun like any stateless container; liveness/readiness probes via HTTP
# /etc/systemd/system/myapp.service
[Unit]
Description=My Bun App
After=network.target
[Service]
Type=simple
User=app
WorkingDirectory=/opt/myapp
ExecStart=/usr/local/bin/bun run start
Restart=on-failure
RestartSec=5s
Environment=NODE_ENV=production
[Install]
WantedBy=multi-user.target
5. Operations
5.1 Package Manager Operations
Lockfile
Bun writes bun.lock as a binary lockfile by default. Enable the human-readable text format:
bun install --save-text-lockfile
Or in bunfig.toml:
[install]
saveTextLockfile = true
CI enforcement — never drift from lockfile:
bun install --frozen-lockfile
Trusted Dependencies
Lifecycle scripts (preinstall, postinstall, etc.) run only for packages listed in trustedDependencies. This is a security feature — untrusted packages cannot execute arbitrary code during install.
{
"trustedDependencies": ["esbuild", "sharp", "@parcel/watcher"]
}
Add trust at install time:
bun add some-package --trust
Vulnerability Audit
# Audit for known vulnerabilities
bun audit
# Machine-readable output
bun audit --json
Patch Management
# Patch a dependency
bun patch <package-name>
# Apply patches
bun patch --commit
5.2 Monitoring & Profiling
Note
CPU and heap profiles are written to the working directory by default. Specify
--cpu-prof-diror--heap-prof-dirto redirect output, especially in production where the working directory may not be writable.
CPU Profiling
# Basic CPU profile (written to <pid>.cpuprofile)
bun --cpu-prof ./server.ts
# Custom name and directory
bun --cpu-prof --cpu-prof-name=server --cpu-prof-dir=/tmp/profiles ./server.ts
# Markdown output for LLM analysis
bun --cpu-prof --cpu-prof-md ./server.ts
# Custom sampling interval (microseconds, default 1000)
bun --cpu-prof --cpu-prof-interval=500 ./server.ts
Heap Profiling
bun --heap-prof ./server.ts
bun --heap-prof --heap-prof-name=server --heap-prof-dir=/tmp/profiles ./server.ts
bun --heap-prof --heap-prof-md ./server.ts
DNS Cache Statistics
const stats = Bun.dns.getCacheStats();
console.log(stats);
// { size, hitCount, missCount, evictionCount }
5.3 Debugging
Bun's debugger is WebSocket-based (Chrome DevTools Protocol). It integrates natively with VS Code via the bun-vscode extension and works with Chrome DevTools.
# Start with inspector (connects immediately)
bun --inspect ./server.ts
# Pause until debugger attaches
bun --inspect-wait ./server.ts
# Break on first line
bun --inspect-brk ./server.ts
# Custom host:port
bun --inspect=0.0.0.0:9229 ./server.ts
VS Code launch configuration:
{
"type": "bun",
"request": "launch",
"name": "Debug server",
"program": "${workspaceFolder}/src/server.ts",
"cwd": "${workspaceFolder}",
"env": {}
}
The BUN_INSPECT_NOTIFY environment variable can be set to a URL that Bun will POST to when the inspector is ready — useful for programmatic debugger attachment in CI.
5.4 Shell Completions
Bun ships completions for bash, zsh, and fish in the completions/ directory (135K chars of completion data). Install them:
# Bash
bun completions >> ~/.bash_completion
# Zsh
bun completions --zsh >> ~/.zshrc
# Fish
bun completions --fish > ~/.config/fish/completions/bun.fish
5.5 File Watching
--watch restarts the process on any change to source files imported by the entry point. --hot reloads changed modules in-place without restarting the process:
# Full restart on change (good for servers with side-effectful startup)
bun --watch ./server.ts
# Module hot-reload (good for stateless handlers)
bun --hot ./server.ts
Warning
--hotdoes not reset module-level state. If your module stores mutable state at the top level (e.g., a singleton cache), that state persists across reloads. Use--watchif you need a clean state on every change.
6. Performance
6.1 DNS Resolution
Bun uses c-ares on Linux for asynchronous DNS resolution, enabling high concurrency without blocking the event loop. On macOS, it uses the system resolver.
- Configure resolution order via
--dns-result-order <verbatim|ipv4first|ipv6first> .localand.localhostdomains always fall back to the system resolver automatically- Pre-connect DNS resolution:
Bun.dns.lookup()accepts abackendoption to select the resolver
6.2 Package Installation Performance
| Backend | Platform | Description |
|---|---|---|
clonefile |
macOS | Copy-on-write via clonefileat() — fastest on APFS |
hardlink |
Linux/macOS | Hard links to cached files — fast, saves disk |
symlink |
All | Symbolic links to cached files |
copyfile |
All | Full file copies — slowest, most compatible |
# Override backend
bun install --backend=hardlink
Network concurrency defaults to 48 parallel requests. Raise for faster installs on high-bandwidth connections:
bun install --network-concurrency=96
6.3 HTTP Server
Bun's HTTP server uses µWebSockets (C layer) with direct kernel I/O. No userspace buffering — responses are written directly via writev. Benchmarks show Bun's Bun.serve() outperforming Node.js/Express and Deno on raw throughput.
Bun.serve({
port: 3000,
fetch(req) {
return new Response("Hello");
},
});
6.4 Memory Tuning
--smol flag: Configures JSC to use a smaller heap. Reduces peak RSS at the cost of more frequent GC pauses and lower throughput. Use in memory-constrained environments (Lambda, small containers):
bun --smol ./server.ts
--zero-fill-buffers: All newly allocated Buffer objects are zero-filled. Adds security at the cost of allocation throughput. Enable for handling untrusted data.
6.5 Bundler Performance Features
- Tree shaking: Dead code elimination enabled by default. Use
--emit-dce-annotationsto annotate output with@__PURE__for downstream tree shakers. - Bytecode:
--bytecodecompiles to JSC bytecode, reducing parse time on cold start. - Minification:
--minifyenables all three: syntax folding, whitespace removal, identifier shortening. Use individually with--minify-syntax,--minify-whitespace,--minify-identifiers. - Code splitting:
--splittinggenerates async chunk boundaries for dynamicimport(). --no-bundle: Transpile-only mode — transforms TypeScript/JSX without bundling. Useful for library builds.
7. Security
Caution
Several flags below weaken security guarantees. They are documented for completeness but should not be used in production without explicit justification.
7.1 TLS Configuration
Bun includes a built-in TLS stack. The CA bundle source is configurable per-process:
| Flag | Behavior |
|---|---|
--use-bundled-ca |
Use Bun's bundled Mozilla CA bundle (default) |
--use-system-ca |
Use the OS certificate store |
--use-openssl-ca |
Use OpenSSL's certificate store |
For mTLS, custom CAs, or self-signed certs in server/client:
const server = Bun.serve({
tls: {
cert: Bun.file("./cert.pem"),
key: Bun.file("./key.pem"),
ca: Bun.file("./ca.pem"),
},
fetch(req) { return new Response("secure"); },
});
7.2 CSRF Protection
Bun.CSRF provides HMAC-based CSRF token generation and verification:
// Generate a token
const token = await Bun.CSRF.generate({
algorithm: "blake2b256", // blake2b256 | blake2b512 | sha256 | sha384 | sha512 | sha512-256
expiry: 3600, // seconds
encoding: "base64",
});
// Verify
const valid = await Bun.CSRF.verify(token);
7.3 Password Hashing
// Hash (bcrypt or argon2)
const hash = await Bun.password.hash("mysecret", {
algorithm: "argon2id",
memoryCost: 65536,
timeCost: 2,
});
// Verify
const ok = await Bun.password.verify("mysecret", hash);
7.4 HTML Escaping
Bun.escapeHTML() escapes &, <, >, ", and ' for HTML contexts. The implementation is SIMD-accelerated:
const safe = Bun.escapeHTML(userInput); // 480 MB/s – 20 GB/s on M1X
7.5 Package Manager Security
- Integrity verification: enabled by default;
--no-verifydisables it (not recommended) - Script sandboxing: lifecycle scripts only run for
trustedDependencies - Audit:
bun auditchecks installed packages against the npm advisory database
7.6 Native Addon Security
--no-addons: disable all native addon (.nodefile) loading- NAPI compatibility shim exists for Node.js-compatible addons; V8 API coverage is partial
- Addons have full native code execution capability — treat them the same as native binaries
7.7 Secrets Management
Do not hardcode secrets in source. Bun supports dotenv loading:
bun --env-file=.env.production ./server.ts
bun --no-env-file ./server.ts # suppress automatic .env loading
For compiled binaries, disable dotenv autoloading if secrets should come from the environment:
bun build --compile --no-compile-autoload-dotenv ./src/index.ts --outfile=myapp
8. Developer Internals
8.1 Repository Structure
oven-sh/bun/
├── src/ # Core Zig/C++ runtime (3.2M chars)
│ ├── cli/ # CLI argument parsing (1.5M chars)
│ ├── bun.js/ # JSC bindings (782K chars)
│ ├── bun.js/api/ # Bun.* API implementations (985K chars)
│ ├── install/ # Package manager (1M chars)
│ ├── bundler/ # JS bundler (576K chars)
│ ├── ast/ # Parser/transpiler (1.6M chars)
│ ├── http/ # HTTP stack (540K chars)
│ ├── shell/ # Shell interpreter (430K chars)
│ ├── css/ # CSS parser (817K chars)
│ ├── resolver/ # Module resolution (401K chars)
│ ├── sql/ # SQL client protocols (94K chars)
│ ├── s3/ # S3 client (184K chars)
│ ├── valkey/ # Redis client (219K chars)
│ ├── md/ # Markdown parser (458K chars)
│ ├── bake/ # SSR framework (402K chars)
│ ├── threading/ # Thread pool (91K chars)
│ ├── async/ # Async primitives (60K chars)
│ ├── io/ # I/O layer (165K chars)
│ ├── sourcemap/ # Source maps (174K chars)
│ ├── glob/ # Glob matching (100K chars)
│ ├── semver/ # Semver parser (102K chars)
│ ├── allocators/ # Custom allocators (64K chars)
│ ├── safety/ # Debug assertions (18K chars)
│ ├── watcher/ # File watcher (34K chars)
│ ├── node-fallbacks/ # Node.js polyfills (170K chars)
│ ├── napi/ # Node-API shim (146K chars)
│ ├── codegen/ # Binding codegen (451K chars)
│ └── errno/ # Error handling (58K chars)
├── packages/ # npm packages
│ ├── bun-types/ # TypeScript definitions (843K chars)
│ ├── bun-vscode/ # VS Code extension
│ ├── bun-lambda/ # Lambda runtime
│ ├── bun-uws/ # µWebSockets (373K chars)
│ ├── bun-native-plugin-rs/ # Rust plugin SDK
│ ├── bun-debug-adapter-protocol/
│ ├── bun-inspector-protocol/
│ ├── bun-wasm/ # WASM transpiler build
│ ├── bun-error/ # Error formatting
│ ├── bun-plugin-svelte/
│ ├── bun-plugin-yaml/
│ └── bun-build-mdx-rs/
├── test/ # Test suite
│ ├── js/ # JavaScript API tests
│ ├── cli/ # CLI integration tests
│ ├── bundler/ # Bundler tests
│ ├── napi/ # NAPI compatibility tests
│ ├── v8/ # V8 compat layer tests
│ ├── internal/ # Internal module tests
│ ├── bake/ # Bake SSR tests
│ ├── docker/ # Docker build tests
│ └── integration/ # End-to-end integration tests
├── bench/ # Benchmark suite
├── docs/ # Documentation (Mintlify format)
├── completions/ # Shell completions (135K chars)
├── dockerhub/ # Docker image configs
├── misctools/ # Debug tools (GDB/LLDB scripts)
├── scripts/ # Build and CI scripts
│ └── build.ts # Build profile definitions
├── patches/ # Third-party patches (zlib, libuv, tinycc, etc.)
└── vendor/ # Vendored dependencies (Zig compiler)
8.2 Build System
Bun uses CMake as the top-level build driver, orchestrated through package.json scripts via Bun itself. The Zig compiler is vendored at vendor/zig/.
Key build scripts:
bun run build:debug # Debug build with ASAN
bun run build:release # Optimized production build
bun run build:ci # CI release build
bun run watch # Incremental zig check (fast feedback)
bun run typecheck # TypeScript type checking
bun run fmt # Prettier (JS/TS) + zig fmt + clang-format
bun run lint # oxlint
bun run test # Full test suite (requires debug build)
bun run test:release # Full test suite (against release build)
Build profiles (defined in scripts/build.ts):
| Profile | Characteristics |
|---|---|
debug |
Debug symbols, ASAN, assertions, no optimization |
release |
-O3, LTO, no assertions, no ASAN |
ci-release |
Like release, CI-specific flags |
release-assertions |
Optimized + runtime assertions |
release-asan |
Optimized + address sanitizer |
debug-no-asan |
Debug symbols, assertions, no ASAN |
8.3 Module System Internals
Bun's module resolver (src/resolver/) supports:
- ESM (
import/export) - CJS (
require()/module.exports) package.jsonexportsfield (conditional exports)package.jsonimportsfield (package self-referencing)tsconfig.jsonpathsandcompilerOptions.baseUrl
Supported loaders (file extension to processing pipeline):
| Loader | Extensions | Description |
|---|---|---|
js |
.js, .mjs, .cjs |
JavaScript |
jsx |
.jsx |
JSX |
ts |
.ts, .mts, .cts |
TypeScript |
tsx |
.tsx |
TypeScript + JSX |
json |
.json, .jsonc |
JSON (parsed, not stringified) |
toml |
.toml |
TOML (parsed to object) |
text |
.txt, .env |
Plain text string |
file |
.png, .woff, etc. |
Asset file (returns URL string) |
wasm |
.wasm |
WebAssembly module |
napi |
.node |
Native addon |
8.4 Codegen System
src/codegen/ contains TypeScript DSL scripts that generate Zig/C++ binding code at build time. This is how Bun keeps the JSC ↔ Zig interface in sync without handwriting FFI boilerplate.
The codegen runs during the build via Node.js (not Bun — a deliberate bootstrapping choice to avoid circular dependency). Generated files are committed to the repository so contributors without a full build environment can work on TypeScript/test code.
8.5 Internal JavaScript Modules
src/js/internal/ and src/js/builtins/ contain JavaScript modules that are bundled into the Bun binary at build time. These implement:
- Node.js built-in module polyfills (wrapped around native implementations)
Bun.*API helpers- Debugger transport (
src/js/internal/debugger.ts) - Stream, event, and async polyfill layers
Internal modules are loaded via a special module specifier (bun:internal/*) and are not accessible to user code.
8.6 Testing
# Run all tests
bun run test
# Run specific test file
bun test test/js/web/fetch.test.ts
# Run tests matching a pattern
bun test --grep "HTTP"
# Run with coverage
bun test --coverage --coverage-reporter=lcov
# Run only changed files (requires git)
bun test --changed
# Update snapshots
bun test --update-snapshots
# Run NAPI compat tests
bun test test/napi/
Test file locations:
| Directory | Contents |
|---|---|
test/js/ |
JavaScript Web API tests |
test/cli/ |
CLI command integration tests |
test/bundler/ |
Bundler correctness tests |
test/napi/ |
Node-API (NAPI) compatibility tests |
test/napi/node-napi-tests/ |
Tests ported directly from Node.js |
test/v8/ |
V8 API compatibility layer tests |
test/internal/ |
Internal module tests |
test/bake/ |
Bake SSR framework tests |
test/docker/ |
Docker build validation |
test/integration/ |
End-to-end integration tests |
test/snapshots/ |
Snapshot fixtures (117 files) |
Test runner: bun test (self-hosted). CI orchestration: node scripts/runner.node.mjs.
8.7 Contributing — Code Quality
Formatting:
bun run fmt # Runs Prettier (JS/TS), zig fmt, clang-format
Linting:
bun run lint # Runs oxlint (config: oxlint.json)
Type checking:
bun run typecheck # tsc --noEmit across packages/bun-types + codegen
Commit practices:
- Keep Zig, C++, and TypeScript changes in separate commits when possible
- Generated files in
src/codegen/are committed — do not.gitignorethem - Large C++ changes to
src/bun.js/require reviewing JSC memory ownership rules
9. Integration Points
9.1 VS Code Extension
packages/bun-vscode provides first-class VS Code integration:
- Debugger: communicates via Debug Adapter Protocol (DAP) and Chrome Inspector Protocol
- Run configurations: launch scripts, tests, and the REPL from the IDE
- package.json schema: inline validation and completion for
bun-specific fields
Install from the VS Code marketplace: search for "Bun" by Oven.
The extension connects to Bun's WebSocket inspector at localhost:6499 by default. Override via bunfig.toml:
[debug]
inspector = "localhost:9229"
9.2 Native Plugins (Rust)
packages/bun-native-plugin-rs is a Rust SDK for writing native bundler plugins that integrate at the NAPI layer:
use bun_native_plugin::*;
#[bun_plugin]
fn my_plugin(builder: PluginBuilder) {
builder.on_load(".custom", |args| {
Ok(OnLoadResult {
contents: "export default 42".into(),
loader: Loader::Js,
})
});
}
Proc macros handle the NAPI registration boilerplate. The compiled .node file is loaded by Bun at runtime.
Note
Native plugins have full access to the Bun process's memory and file system. Only load trusted plugins. Consider using JavaScript plugins (the
plugin()API) for untrusted or community plugins — they run in the same JS sandbox as user code.
9.3 Node.js Compatibility
Bun maintains extensive Node.js API compatibility via polyfills in src/node-fallbacks/ and src/js/internal/. The following Node.js built-in modules are supported:
| Module | Notes |
|---|---|
process |
Full compatibility including process.env, process.argv, signals |
Buffer |
Full compatibility; --zero-fill-buffers supported |
fs / fs/promises |
Full async/sync API |
path |
Full compatibility |
http / https |
Backed by Bun's HTTP stack |
crypto |
Backed by BoringSSL/WebCrypto |
stream / stream/promises |
Node.js streams + pipeline |
events |
EventEmitter |
child_process |
spawn, exec, execSync, spawnSync |
worker_threads |
Web Worker-based implementation |
net / tls |
TCP/TLS sockets |
dns / dns/promises |
Backed by c-ares (Linux) / system (macOS) |
os |
System information |
url / querystring |
URL parsing + WHATWG URL |
util |
promisify, inspect, format, etc. |
assert |
Node.js assertion library |
zlib |
Compression (gzip, deflate, brotli) |
V8 API compatibility: A partial V8 API shim is available for native addons that use V8 internal APIs. Coverage is not complete — addons built against Node.js internals may not load. Use --no-addons to disable addon loading if addon compatibility issues cause crashes.
9.4 Bun Shell Integration
Bun.$ provides a cross-platform shell for scripting in JavaScript/TypeScript that uses Bun's built-in shell interpreter (src/shell/):
import { $ } from "bun";
// Basic command
const result = await $`ls -la`;
// Interpolation (safe, no injection)
const dir = "./src";
const files = await $`find ${dir} -name "*.ts"`.text();
// Pipe
const count = await $`cat package.json | wc -l`.text();
// Environment
await $`echo $HOME`.env({ HOME: "/custom" });
The shell is not /bin/sh — it is Bun's own interpreter with a POSIX-compatible subset. Redirects, pipes, and basic globbing are supported. Use --shell=system with bun run to use the system shell for package.json scripts.
9.5 Built-in Database Clients
Bun includes built-in clients for PostgreSQL, MySQL, and Redis/Valkey — no npm package required.
PostgreSQL:
import { sql } from "bun";
const rows = await sql`SELECT * FROM users WHERE id = ${userId}`;
Connection is configured via DATABASE_URL, POSTGRES_URL, PGURL, or explicit PGHOST/PGUSER/PGPASSWORD environment variables.
MySQL:
import { sql } from "bun";
// Configured via MYSQL_URL or MYSQL_HOST/PORT/USER/PASSWORD/DATABASE
const rows = await sql`SELECT * FROM products LIMIT 10`;
Redis / Valkey:
import { Redis } from "bun";
const redis = new Redis(); // uses REDIS_URL or VALKEY_URL
await redis.set("key", "value");
const val = await redis.get("key");
Pre-connect at startup for reduced first-request latency:
bun --redis-preconnect --sql-preconnect ./server.ts
9.6 S3 Client
import { S3Client } from "bun";
const s3 = new S3Client({
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
bucket: "my-bucket",
region: "us-east-1",
});
// Write
await s3.write("path/to/file.txt", "content");
// Read
const file = s3.file("path/to/file.txt");
const text = await file.text();
9.7 Bake (Experimental SSR Framework)
src/bake/ implements Bun's experimental server-side rendering framework. Enable via:
bun build --app ./src/index.tsx
Server components support:
bun build --app --server-components ./src/index.tsx
Warning
Bake is experimental. The API surface is not stable and may change without notice between Bun releases. Do not use in production systems without pinning to a specific Bun version and auditing changes at each upgrade.
9.8 Markdown Parser
src/md/ implements a GFM-compatible markdown parser that outputs HTML, ANSI-colored terminal output, or React elements:
import { Markdown } from "bun";
const html = Markdown.toHTML("# Hello\n\n**world**");
const ansi = Markdown.toANSI("# Hello\n\n**world**");