Contributing
Contributing to Bun
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
bun bdnix 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 rubysudo apt install curl wget lsb-release software-properties-common cmake git golang libtool ninja-build pkg-config ruby-full xz-utilssudo pacman -S base-devel cmake git go libiconv libtool make ninja pkg-config python rustup sed unzip rubysudo dnf install clang21 llvm21 lld21 cmake git golang libtool ninja-build pkg-config ruby libatomic-static libstdc++-static sed unzip which libicu-devel 'perl(Math::BigInt)'sudo zypper install go cmake ninja automake git icu rustupBun 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 | shBefore 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 | bashnpm install -g bunbrew tap oven-sh/bun
brew install bunOptional: 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 ccacheThe 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# LLVM has an automatic installation script that is compatible with all versions of Ubuntu
wget https://apt.llvm.org/llvm.sh -O - | sudo bash -s -- 21 allsudo pacman -S llvm clang lldsudo dnf install llvm clang lld-develsudo zypper install clang21 lld21 llvm21If none of these work, install it manually.
Make sure Clang/LLVM 21 is in your path:
which clang-21If 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"# use fish_add_path if you're using fish
export PATH="$PATH:/usr/lib/llvm21/bin"⚠️ 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 downloads and builds dependencies.
bun run buildThe 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_debugVSCode#
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-debugRunning 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.tsA 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>(orbun run rust:checkfor the whole workspace) to type-check Rust changes without linking.bun run watchrunscargo checkon 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>=1enables debug logging for the correspondingdeclare_scope!(<scope>, ...)/scoped_log!(<scope>, ...)logs. SetBUN_DEBUG_QUIET_LOGS=1to disable all debug logging that isn't explicitly enabled. To dump debug logs into a file, setBUN_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-- Generatesbuild/debug/codegen/JSSink.cpp,build/debug/codegen/JSSink.hwhich implement various classes for interfacing withReadableStream. This is internally howFileSink,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.tsfiles 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 likenode:fs,bun:ffiinto files included in the final binary. In development, these can be reloaded without rebuilding native code (you still need to runbun 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 likeReadableStreamandWritableStream. 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:releaseThe 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 onlybun-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 --versionYou 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:asanCI 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 version pinned in WEBKIT_VERSION in scripts/build/deps/webkit.ts
# (a commit sha or an autobuild-* release tag; this handles both)
bun sync-webkit-source
# Build bun with the local JSC build — this automatically configures and builds JSC
bun run build:localbun 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
CompilationDatabaseline in.clangdconfig should beCompilationDatabase: 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 or release tag.
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 bun run build may fail with the following error:
fatal error: 'span' file not found
#include <span>
^~~~~~The issue may also surface when first running bun run build, 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++-11Then 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 100libarchive#
If you see an error on macOS when compiling libarchive, run:
brew install pkg-configmacOS library not found for -lSystem#
If you see this error when compiling, run:
xcode-select --installCannot 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 --static-libatomic=offThe 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 enablescoped_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.tsis in/tmp/bun-debug-src/home/bun/index.ts
Contributing to the docs#
The docs are the MDX files in docs/ in the Bun repository. bun.com/docs is built from them.
Voice#
A docs page describes what Bun does and what you can do with it. Write it so that a developer who is new to Bun can read the page once and act on it. These guidelines are adapted from the Next.js docs contribution guide:
- Write short sentences that each make one point. If a sentence needs several commas or a parenthetical, split it up or turn it into a list.
- Use plain words: "use" rather than "utilize", "to" rather than "in order to". Cut filler such as "Note that" and "Please".
- Use the active voice and name the actor: "Bun reads
bunfig.toml", rather than "bunfig.tomlis read". A sentence built around "is" and "by" is usually passive. - Describe current behavior in the present tense: "Bun installs the package", rather than "Bun will install the package".
- Name the subject when "this" or "it" could refer to more than one thing: "
--isolateis how Jest behaves by default", rather than "This is how Jest behaves by default". - Address the reader as "you", and make Bun (or the specific tool) the other actor: "Bun caches the tarball", rather than "we cache the tarball" or "let's cache the tarball".
- Leave out "easy", "simple", "just", and "quick". They add nothing when a task is easy and discourage readers when it is not. State the concrete property instead: "one command", "no configuration".
- Say what to do rather than what to avoid: "use
port: 0so the operating system picks a free port", rather than "don't hardcode ports". State limitations plainly; a limitation is a fact, not a warning to the reader. - Use gender-neutral language: "developers", "users", "they".
- Make link text name its destination: "see
bun install", rather than "see here". - Run every code example before you publish it, and check option names and defaults against the implementation on
main.