This bundles cli.ts into an executable you can run directly:
terminal
./mycli
Hello world!
All imported files and packages are bundled into the executable, along with a copy of the Bun runtime. All built-in Bun and Node.js APIs are supported.
Use the --target flag to compile your standalone executable for a different operating system, architecture, or version of Bun than the machine you’re running bun build on.To build for Linux x64 (most servers):
CLI
JavaScript
terminal
bun build --compile --target=bun-linux-x64 ./index.ts --outfile myapp# To support CPUs from before 2013, use the baseline version (nehalem)bun build --compile --target=bun-linux-x64-baseline ./index.ts --outfile myapp# To explicitly only support CPUs from 2013 and later, use the modern version (haswell)# modern is faster, but baseline is more compatible.bun build --compile --target=bun-linux-x64-modern ./index.ts --outfile myapp
bun build --compile --target=bun-windows-x64 ./path/to/my/app.ts --outfile myapp# To support CPUs from before 2013, use the baseline version (nehalem)bun build --compile --target=bun-windows-x64-baseline ./path/to/my/app.ts --outfile myapp# To explicitly only support CPUs from 2013 and later, use the modern version (haswell)bun build --compile --target=bun-windows-x64-modern ./path/to/my/app.ts --outfile myapp# note: if no .exe extension is provided, Bun adds it automatically for Windows executables
build.ts
// Standard Windows x64await Bun.build({ entrypoints: ["./path/to/my/app.ts"], compile: { target: "bun-windows-x64", outfile: "./myapp", // .exe added automatically },});// Baseline or modern variantsawait Bun.build({ entrypoints: ["./path/to/my/app.ts"], compile: { target: "bun-windows-x64-baseline", outfile: "./myapp", },});
To build for Windows arm64:
CLI
JavaScript
terminal
bun build --compile --target=bun-windows-arm64 ./path/to/my/app.ts --outfile myapp# note: if no .exe extension is provided, Bun adds it automatically for Windows executables
The segments of the --target value can appear in any order, as long as they’re delimited by -.
—target
Operating System
Architecture
Modern
Baseline
Libc
bun-linux-x64
Linux
x64
✅
✅
glibc
bun-linux-arm64
Linux
arm64
✅
N/A
glibc
bun-windows-x64
Windows
x64
✅
✅
-
bun-windows-arm64
Windows
arm64
✅
N/A
-
bun-darwin-x64
macOS
x64
✅
✅
-
bun-darwin-arm64
macOS
arm64
✅
N/A
-
bun-linux-x64-musl
Linux
x64
✅
✅
musl
bun-linux-arm64-musl
Linux
arm64
✅
N/A
musl
On x64 platforms, Bun uses SIMD optimizations that require a CPU with AVX2 instructions. The -baseline build of Bun
is for older CPUs without them. The Bun installer detects which version to use, but when cross-compiling you might not
know the target CPU. This mostly matters on Windows x64 and Linux x64, rarely on Darwin x64. If you or your users see
"Illegal instruction" errors, you might need to use the baseline version.
Compiled executables reduce memory usage and improve Bun’s start time.Normally, Bun reads and transpiles JavaScript and TypeScript files on import and require. This is part of what makes so much of Bun “just work”, but it’s not free: reading files from disk, resolving paths, parsing, transpiling, and printing source code costs time and memory.Compiled executables move that cost from runtime to build time.When deploying to production, we recommend the following:
CLI
JavaScript
terminal
bun build --compile --minify --sourcemap ./path/to/my/app.ts --outfile myapp
Bytecode compilation moves parsing overhead for large input files from runtime to bundle time. Your app starts faster, in exchange for making the bun build command a little slower. It doesn’t obscure source code.
Bytecode compilation supports both cjs and esm formats when used with --compile.
The --minify argument reduces the size of the transpiled output code. For a large application, this can save megabytes of space. For smaller applications, it might still improve start time a little.The --sourcemap argument embeds a sourcemap compressed with zstd, so that errors & stacktraces point to their original locations instead of the transpiled location. Bun decompresses & resolves the sourcemap automatically when an error occurs.The --bytecode argument enables bytecode compilation. Every time you run JavaScript code in Bun, JavaScriptCore (the engine) compiles your source code into bytecode. --bytecode moves that parsing work from runtime to bundle time, which shortens startup.
Standalone executables can automatically load configuration files from the directory where they are run. By default:
tsconfig.json and package.json loading is disabled — these are typically only needed at development time, and the bundler already uses them when compiling
.env and bunfig.toml loading is enabled — these often contain runtime configuration that may vary per deployment
In a future version of Bun, .env and bunfig.toml may also be disabled by default for more deterministic behavior.
Set the BUN_BE_BUN=1 environment variable to run a standalone executable as if it were the bun CLI itself. The executable ignores its bundled entrypoint and exposes the full bun CLI instead.For example, consider an executable compiled from this script:
terminal
echo "console.log(\"you shouldn't see this\");" > such-bun.jsbun build --compile ./such-bun.js
[3ms] bundle 1 modules[89ms] compile such-bun
Normally, running ./such-bun with arguments executes the script.
terminal
# Executable runs its own entrypoint by default./such-bun install
you shouldn't see this
However, with the BUN_BE_BUN=1 environment variable, it acts like the bun binary:
terminal
# With the env var, the executable acts like the `bun` CLIBUN_BE_BUN=1 ./such-bun install
bun install v1.2.16-canary.1 (1d1db811)Checked 63 installs across 64 packages (no changes) [5.00ms]
CLI tools built on top of Bun can use this to install packages, bundle dependencies, or run other files without downloading a separate binary or installing Bun.
The --compile flag can create a standalone executable that contains both server and client code, which suits full-stack applications. When you import an HTML file in your server code, Bun bundles the frontend assets (JavaScript, CSS, and so on) and embeds them into the executable.
import { serve } from "bun";import index from "./index.html";const server = serve({ routes: { "/": index, "/api/hello": { GET: () => Response.json({ message: "Hello from API" }) }, },});console.log(`Server running at http://localhost:${server.port}`);
This creates a self-contained binary that includes:
Your server code
The Bun runtime
All frontend assets (HTML, CSS, JavaScript)
Any npm packages used by your server
The result is a single file you can deploy anywhere without installing Node.js, Bun, or any dependencies:
terminal
./myapp
Bun serves the frontend assets with the correct MIME types and cache headers. The HTML import is replaced with a manifest object that Bun.serve uses to serve the pre-bundled assets.For more on building full-stack applications, see the full-stack guide.
console.log("Hello from Bun!");// Any of these will work:new Worker("./my-worker.ts");new Worker(new URL("./my-worker.ts", import.meta.url));new Worker(new URL("./my-worker.ts", import.meta.url).href);
When you add multiple entrypoints to a standalone executable, each is bundled separately into the executable.We may eventually detect statically-known paths in new Worker(path) and bundle them automatically, but for now you need to list the worker file as an entrypoint, as in the earlier example.If you use a relative path to a file not included in the standalone executable, Bun loads that path from disk relative to the process’s current working directory, and errors if it doesn’t exist.
Standalone executables can embed files directly into the binary, so a single executable can ship images, JSON configs, templates, or any other assets your application needs.
import icon from "./icon.png" with { type: "file" };console.log(icon);// During development: "./icon.png"// After compilation: "$bunfs/icon-a1b2c3d4.png" (internal path)
The import returns a path string that points to the embedded file. At build time, Bun:
Reads the file contents
Embeds the data into the executable
Replaces the import with an internal path (prefixed with $bunfs/)
You can then read this embedded file using Bun.file() or Node.js fs APIs.
To embed a SQLite database into the compiled executable, set type: "sqlite" in the import attribute and the embed attribute to "true".The database file must already exist on disk. Then, import it in your code:
index.ts
import myEmbeddedDb from "./my.db" with { type: "sqlite", embed: "true" };console.log(myEmbeddedDb.query("select * from users LIMIT 1").get());
Finally, compile it into a standalone executable:
terminal
bun build --compile ./index.ts --outfile mycli
The database file must exist on disk when you run bun build --compile. The embed: "true" attribute tells the
bundler to include the database contents inside the compiled executable. When running normally with bun run, the
database file is loaded from disk just like a regular SQLite import.
In the compiled executable, the embedded database is read-write, but all changes are lost when the executable exits (since it’s stored in memory).
To embed a directory with bun build --compile, include file patterns in your build:
CLI
JavaScript
terminal
bun build --compile ./index.ts ./public/**/*.png
build.ts
import { Glob } from "bun";// Expand glob pattern to file listconst glob = new Glob("./public/**/*.png");const pngFiles = Array.from(glob.scanSync("."));await Bun.build({ entrypoints: ["./index.ts", ...pngFiles], compile: { outfile: "./myapp", },});
Then, you can reference the files in your code:
index.ts
import icon from "./public/assets/icon.png" with { type: "file" };import { file } from "bun";export default { fetch(req) { // Embedded files can be streamed from Response objects return new Response(file(icon)); },};
This is a workaround, and we expect to replace it with a more direct API.
Use Bun.isStandaloneExecutable to check whether the current process is running from a compiled binary:
index.ts
if (Bun.isStandaloneExecutable) { // Running from `bun build --compile` output} else { // Running via `bun <file>` or as a library}
Unlike Bun.embeddedFiles.length > 0, this check does not allocate Blob objects for each embedded file, so it is safe to call at startup in binaries that embed large assets.
By default, embedded files have a content hash appended to their name, which helps with cache invalidation when you serve them from a URL or CDN. To keep the original name instead, configure asset naming:
CLI
JavaScript
terminal
bun build --compile --asset-naming="[name].[ext]" ./index.ts
Example use case - embedding environment config at build time:
cli.ts
import config from "./config.env.json";console.log(`Running in ${config.environment} mode`);console.log(`API endpoint: ${config.apiUrl}`);
Plugins can perform any transformation: compile YAML/TOML configs, inline SQL queries, generate type-safe API clients, or preprocess templates. See the plugin documentation.
// Simple boolean - compile for current platform (uses entrypoint name as output)compile: true// Target string - cross-compile (uses entrypoint name as output)compile: "bun-linux-x64"// Full options object - specify outfile and other optionscompile: { target: "bun-linux-x64", outfile: "./myapp",}