Skip to main content
Configuring a development environment for Bun can take 10-30 minutes depending on your internet connection and computer speed. You will need ~10GB of free disk space for the repository and build artifacts. If you are using Windows, see Building Windows.

Using Nix (Alternative)

The repository includes a Nix flake as an alternative to installing dependencies manually:
nix develop
# or explicitly use the pure shell
# nix develop .#pure
export CMAKE_SYSTEM_PROCESSOR=$(uname -m)
bun bd
nix develop provides all dependencies in an isolated, reproducible environment without requiring sudo.

Install Dependencies (Manual)

Using your system’s package manager, install Bun’s dependencies:
brew install automake ccache cmake coreutils gnu-sed go icu4c libiconv libtool ninja pkg-config rustup-init ruby
Bun is written in Rust and requires a specific nightly toolchain (pinned in rust-toolchain.toml). Install Rust with rustup rather than your distro’s rust/cargo packages — the build scripts use rustup to automatically install and update the pinned nightly:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Before starting, install a release build of Bun: the build uses Bun’s bundler to transpile and minify code, and to run the code generation scripts.
curl -fsSL https://bun.com/install | bash

Optional: Install ccache

ccache caches compilation artifacts, which speeds up rebuilds:
# For macOS
brew install ccache

# For Ubuntu/Debian
sudo apt install ccache

# For Arch
sudo pacman -S ccache

# For Fedora
sudo dnf install ccache

# For openSUSE
sudo zypper install ccache
The build scripts detect and use ccache automatically if it’s available. Check cache statistics with ccache --show-stats.

Install LLVM

Bun requires LLVM 21.1.8 (clang is part of LLVM). The build system enforces this version: a mismatched version causes memory allocation failures at runtime. In most cases, you can install LLVM through your system package manager:
brew install llvm@21
If none of these work, install it manually. Make sure Clang/LLVM 21 is in your path:
which clang-21
If not, add it manually:
# use fish_add_path if you're using fish
# use path+="$(brew --prefix llvm@21)/bin" if you are using zsh
export PATH="$(brew --prefix llvm@21)/bin:$PATH"
⚠️ On Ubuntu <= 20.04, you may need to install the C++ standard library separately. See the troubleshooting section.

Building Bun

After cloning the repository, run the following command to build. This can take a while: it clones submodules and builds dependencies.
bun run build
The binary is at ./build/debug/bun-debug. It is recommended to add this to your $PATH. To verify the build worked, print its version:
build/debug/bun-debug --version
x.y.z_debug

VSCode

VSCode is the recommended IDE for working on Bun; the repository includes configuration for it. After opening the repository, run Extensions: Show Recommended Extensions to install the recommended extensions for Rust and C++. rust-analyzer picks up the workspace Cargo.toml automatically and uses the pinned toolchain in rust-toolchain.toml for analysis, so diagnostics match the build. If you use a different editor, point rust-analyzer (or your editor’s Rust plugin) at the repo root — the Cargo workspace and rust-toolchain.toml are discovered automatically. We recommend adding ./build/debug to your $PATH so that you can run bun-debug in your terminal:
bun-debug

Running debug builds

The bd package.json script compiles and runs a debug build of Bun, only printing the output of the build process if it fails.
bun bd <args>
bun bd test foo.test.ts
bun bd ./foo.ts
A full debug build can take a few minutes when Rust or C++ has changed; cargo’s incremental compilation makes subsequent Rust-only rebuilds much faster. If your development workflow is “change one line, save, rebuild”, you will still spend too much time waiting for the link step. Instead:
  • Batch up your changes
  • Use cargo check -p <crate> (or bun run rust:check for the whole workspace) to type-check Rust changes without linking. bun run watch runs cargo check on every save.
  • Ensure rust-analyzer is running for inline diagnostics (the recommended VSCode extensions set this up)
  • Prefer using the debugger (“CodeLLDB” in VSCode) to step through the code.
  • Use debug logs. BUN_DEBUG_<scope>=1 enables debug logging for the corresponding declare_scope!(<scope>, ...) / scoped_log!(<scope>, ...) logs. Set BUN_DEBUG_QUIET_LOGS=1 to disable all debug logging that isn’t explicitly enabled. To dump debug logs into a file, set BUN_DEBUG=<path-to-file>.log. Debug logs are removed in release builds.
  • src/js/**.ts changes rebuild almost instantly. Single-crate Rust changes and C++ changes are incremental; only the final link is unavoidable.

Code generation scripts

Bun’s build process runs several code generation scripts automatically when certain files change:
  • ./src/codegen/generate-jssink.ts — Generates build/debug/codegen/JSSink.cpp, build/debug/codegen/JSSink.h which implement various classes for interfacing with ReadableStream. This is internally how FileSink, ArrayBufferSink, "type": "direct" streams and other code related to streams work.
  • ./src/codegen/generate-classes.ts — Generates Rust & C++ bindings for JavaScriptCore classes implemented in Rust. **/*.classes.ts files define the interfaces for classes, methods, prototypes, and getters/setters; the code generator reads them to generate the boilerplate that implements the JavaScript objects in C++ and wires them up to Rust.
  • ./src/codegen/cppbind.ts — Scans the C++ bindings for functions marked with an export attribute and generates automatic Rust FFI wrappers (cpp.rs) for them.
  • ./src/codegen/bundle-modules.ts — Bundles built-in modules like node:fs, bun:ffi into files included in the final binary. In development, these can be reloaded without rebuilding native code (you still need to run bun run build, but it re-reads the transpiled files from disk afterwards). In release builds, these are embedded into the binary.
  • ./src/codegen/bundle-functions.ts — Bundles globally-accessible functions implemented in JavaScript/TypeScript like ReadableStream and WritableStream. These are used similarly to the builtin modules, but the output more closely aligns with what WebKit/Safari does for Safari’s built-in functions, so implementations can be copy-pasted from WebKit as a starting point.

Modifying ESM modules

Certain modules like node:fs, node:stream, bun:sqlite, and ws are implemented in JavaScript. These live in src/js/{node,bun,thirdparty} files and are pre-bundled using Bun.

Release build

To compile a release build of Bun, run:
bun run build:release
The binaries are at ./build/release/bun and ./build/release/bun-profile.

Download release build from pull requests

You can run the release build from a pull request without building it locally, which is useful for manually testing changes before they are merged. Use the bun-pr npm package:
bunx bun-pr <pr-number>
bunx bun-pr <branch-name>
bunx bun-pr "https://github.com/oven-sh/bun/pull/1234566"
bunx bun-pr --asan <pr-number> # Linux x64 only
bun-pr downloads the release build from the pull request’s GitHub Actions artifacts and adds it to $PATH as bun-${pr-number}, so you can run it directly:
bun-1234566 --version
You may need the gh CLI installed to authenticate with GitHub.

Viewing CI failures from the terminal

Bun’s CI runs on BuildKite. Install the BuildKite CLI (brew install buildkite/buildkite/bk) and set BUILDKITE_API_TOKEN to a read-scoped API token. The repo includes a .bk.yaml so bk commands default to the bun pipeline.
bun run ci:status         # progress summary for the current branch's latest build
bun run ci:errors         # rendered test-failure output, tagged [new] vs [also on main]
bun run ci:logs           # save full logs for each failed job to ./tmp/ci-<build>/
bun run ci:watch          # watch until the build finishes
bun run ci:find           # print the build number (compose with raw `bk`)
All of these accept a target: #1234 (PR number), a PR URL, a branch name, or a build number. Without one they use the current git branch.

AddressSanitizer

AddressSanitizer helps find memory issues, and is enabled by default in debug builds of Bun on Linux and macOS. This covers the Rust code, the C++ bindings, and all dependencies. It makes the build take about 2x longer; if that’s stopping you from being productive you can disable it with bun run build:debug:noasan (or pass --asan=off to scripts/build.ts), but generally we recommend batching your changes up between builds. To build a release build with AddressSanitizer, run:
bun run build:asan
CI runs the test suite with at least one target built with AddressSanitizer.

Building WebKit locally + Debug mode of JSC

WebKit is not cloned by default (to save time and disk space). To clone and build WebKit locally, run:
# Clone WebKit into ./vendor/WebKit
git clone https://github.com/oven-sh/WebKit vendor/WebKit

# Check out the commit hash specified in WEBKIT_VERSION in scripts/build/deps/webkit.ts
git -C vendor/WebKit checkout <commit_hash>

# Build bun with the local JSC build — this automatically configures and builds JSC
bun run build:local
bun run build:local handles everything: configuring JSC, building JSC, and building Bun. On subsequent runs, JSC rebuilds incrementally if any WebKit sources changed. ninja -Cbuild/debug-local also works after the first build, and builds both Bun and JSC. The build output goes to ./build/debug-local (instead of ./build/debug), so you’ll need to update a couple of places:
  • The first line in src/js/builtins.d.ts
  • The CompilationDatabase line in .clangd config should be CompilationDatabase: build/debug-local
  • In .vscode/launch.json, many configurations use ./build/debug/, change them as you see fit
The WebKit folder, including build artifacts, is 8GB+ in size. If you are using a JSC debug build with VSCode, run the C/C++: Select a Configuration command so IntelliSense finds the debug headers. If you make changes to Bun’s WebKit fork, you also have to change WEBKIT_VERSION in scripts/build/deps/webkit.ts to point to your commit hash.

Troubleshooting

’span’ file not found on Ubuntu

⚠️ These instructions are specific to Ubuntu. The same issues are unlikely on other Linux distributions.
Clang uses libstdc++, the C++ standard library implementation provided by the GNU Compiler Collection (GCC), by default. Clang can link against libc++ instead, but that requires explicitly passing the -stdlib flag. Bun relies on C++20 features like std::span, which are not available in GCC versions lower than 11. As a result, running make setup may fail with the following error:
fatal error: 'span' file not found
#include <span>
         ^~~~~~
The issue may also surface when first running bun setup, with Clang unable to compile a simple program:
The C++ compiler

  "/usr/bin/clang++-21"

is not able to compile a simple test program.
To fix the error, update GCC to version 11. It may be available in your distribution’s official repositories; otherwise, add a third-party repository that provides GCC 11 packages:
sudo apt update
sudo apt install gcc-11 g++-11
# If the above command fails with `Unable to locate package gcc-11` we need
# to add the APT repository
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
# Now run `apt install` again
sudo apt install gcc-11 g++-11
Then set GCC 11 as the default compiler:
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 100
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-11 100

libarchive

If you see an error on macOS when compiling libarchive, run:
brew install pkg-config

macOS library not found for -lSystem

If you see this error when compiling, run:
xcode-select --install

Cannot find libatomic.a

Bun defaults to linking libatomic statically, as not all systems have it. If you are building on a distro that does not have a static libatomic available, enable dynamic linking with:
bun run build -DUSE_STATIC_LIBATOMIC=OFF
The built version of Bun may not work on other systems if compiled this way.

Using bun-debug

  • Disable logging: BUN_DEBUG_QUIET_LOGS=1 bun-debug ... (to disable all debug logging)
  • Enable logging for a specific scope: BUN_DEBUG_EventLoop=1 bun-debug ... (to enable scoped_log!(EventLoop, ...) output)
  • Bun transpiles every file it runs. To see the actual executed source in a debug build, find it in /tmp/bun-debug-src/...path/to/file. For example, the transpiled version of /home/bun/index.ts is in /tmp/bun-debug-src/home/bun/index.ts