On Runtime, Bun needs no install step, and the SDK runs on Bun too. Every sandbox is a Firecracker microVM with its own kernel. Bun 1.4.0 is installed next to Node.js 24.21.0. A 2 vCPU, 4 GiB sandbox costs $0.08 an hour with both CPUs busy, and $0.03125 an hour while it waits. Runtime bills the CPU your code uses (pricing).
Run a Bun script
TypeScriptimport { Sandbox } from "withruntime";const script = `const words = "the quick brown fox jumps over the lazy dog".split(" ");const longest: string = words.reduce((a, b) => (b.length > a.length ? b : a));console.log(longest, Bun.version);`;await using sbx = await Sandbox.create({ network: { internet: false }, timeoutSeconds: 300, onLeaseEnd: "stop",});await sbx.files.write("/workspace/job.ts", script);const run = await sbx.exec(["bun", "job.ts"], { timeoutMs: 20_000 });console.log(run.exitCode, run.stdout);Pythonfrom withruntime import Sandboxscript = """const words = "the quick brown fox jumps over the lazy dog".split(" ");const longest: string = words.reduce((a, b) => (b.length > a.length ? b : a));console.log(longest, Bun.version);"""with Sandbox.create(network={"internet": False}, timeout_seconds=300, on_lease_end="stop") as sbx: sbx.files.write("/workspace/job.ts", script) run = sbx.exec(["bun", "job.ts"], timeout_ms=20_000) print(run.exit_code, run.stdout)Bun "supports TypeScript and JSX with no configuration". It transpiles each
file before it runs (Bun). Bun does not
type-check. For tsc, see TypeScript in a sandbox.
What is in the image
| Tool | Version or detail |
|---|---|
| Bun | 1.4.0 (bun) |
| Node.js | 24.21.0 (node, npm, npx) |
unzip |
In the image; Bun's own installer needs it on Linux |
| Build tools | gcc, g++, make |
| OS | Ubuntu 24.04.5 LTS (noble), amd64, 6.18 kernel |
Commands run as the user runtime, with /workspace as the home and working
directory. sudo works without a password
(the sandbox environment).
Run a test suite with bun test
bun test is Bun's built-in runner, and it is compatible with Jest. It finds
files named *.test.ts, *_test.ts, *.spec.ts and *_spec.ts, and the
JavaScript forms of those names. Each test times out after 5,000 ms unless you
pass --timeout (Bun test).
TypeScriptimport { Sandbox } from "withruntime";await using sbx = await Sandbox.create({ timeoutSeconds: 1200, onLeaseEnd: "stop" });await sbx.files.upload("./my-app", "/workspace/app");await sbx.exec("cd app && bun install", { check: true, timeoutMs: 300_000 });await sbx.network.set({ internet: false }); // tests run with no networkconst run = await sbx.exec(["bun", "test", "--timeout", "20000"], { cwd: "/workspace/app", timeoutMs: 600_000, onStdout: (text) => process.stdout.write(text),});process.exitCode = run.exitCode ?? 1;Pythonimport sysfrom withruntime import Sandboxwith Sandbox.create(timeout_seconds=1200, on_lease_end="stop") as sbx: sbx.files.upload("./my-app", "/workspace/app") sbx.exec("cd app && bun install", check=True, timeout_ms=300_000) sbx.network.set(internet=False) # tests run with no network run = sbx.exec(["bun", "test", "--timeout", "20000"], cwd="/workspace/app", timeout_ms=600_000, on_stdout=sys.stdout.write) sys.exit(1 if run.exit_code is None else run.exit_code)With timeoutMs over 60 seconds, output streams, and the result keeps
everything the tests printed. Cutting the network after the install keeps a
test that calls out from reaching anything.
Serve a Bun app
Start a server with spawn, not exec. Everything that exec starts ends when
its command ends. Then share the port as a private HTTPS
preview.
TypeScriptimport { Sandbox } from "withruntime";await using sbx = await Sandbox.create({ timeoutSeconds: 1800 });await sbx.files.write( "/workspace/server.ts", 'Bun.serve({ port: 3000, fetch: () => new Response("hello from bun") });',);const server = await sbx.spawn(["bun", "server.ts"]);await sbx.exec("sleep 1 && curl -s localhost:3000", { check: true });console.log(server.id);A newer Bun, or the same Bun in every sandbox
Bun's installer puts Bun in ~/.bun/bin. It takes a version as
bash -s "bun-v<version>", and it needs unzip
(Bun installation). To pin one release
for every sandbox, run the installer in a custom image recipe.
Set BUN_INSTALL to /usr/local, so bun lands on the sandbox user's
PATH:
TypeScriptimport { Runtime } from "withruntime";const runtime = new Runtime();await runtime.images.build({ name: "bun-app", recipe: { commands: ["curl -fsSL https://bun.com/install | BUN_INSTALL=/usr/local bash"], npm: ["zod"], },});await using sbx = await runtime.sandboxes.create({ image: "bun-app" });console.log((await sbx.exec(["bun", "--version"])).stdout);Pythonfrom withruntime import Runtimeruntime = Runtime()runtime.images.build(name="bun-app", recipe={ "commands": ["curl -fsSL https://bun.com/install | BUN_INSTALL=/usr/local bash"], "npm": ["zod"],})with runtime.sandboxes.create(image="bun-app") as sbx: print(sbx.exec(["bun", "--version"]).stdout)Recipe commands run as root in /workspace. Building an image is free. A
stored image is charged by its size.
Drive sandboxes from Bun
The withruntime package supports Bun as well as Node 22.12 or later. await using works on Bun. A Bun service can therefore create, run and delete
sandboxes with the samples above, unchanged (JavaScript SDK).
Terminalbun add withruntimeRelated
- Run TypeScript code in a sandbox
- Run JavaScript and Node.js code in a sandbox
- Preview apps an agent built
- Run agent evals and SWE-bench
A new account gets 100 free hours of a 1 GB machine in its first 30 days, with no card required:
Terminalnpx withruntime sandboxes run -- bun --versionSources
- Bun runtime: https://bun.com/docs/runtime (read 26 October 2026)
- Bun test runner: https://bun.com/docs/test (read 26 October 2026)
- Bun installation: https://bun.com/docs/installation (read 26 October 2026)
bun add: https://bun.com/docs/cli/add (read 26 October 2026)
Facts on this page were checked on 26 October 2026.