On Runtime there is nothing to install. gcc and make are in every sandbox. Each sandbox is a Firecracker microVM running Ubuntu 24.04.5 with build-essential. A C file compiles in the first command after the sandbox is created. This makes a fresh machine for each program practical.
Why C needs a real boundary
C code can overflow a buffer, write through a stray pointer, fork until the process table is full, or spin forever. In a container, all of that runs on the host's kernel. In a Runtime sandbox, it runs on the microVM's own kernel. CPU, memory, network and cost are enforced on the host, outside the VM (security). The worst a bad program can do is break a machine you were going to throw away.
What is installed and what to add
A new sandbox has no apt package lists yet. Run sudo apt-get update once
before the first apt-get install below.
| Tool | In the default image | Ubuntu 24.04 package, checked 26 October 2026 | Add it with |
|---|---|---|---|
| gcc, make | Yes (build-essential) | gcc-13 13.3.0, build-essential 12.10 | nothing |
| clang | No | clang-18 18.1.3 | sudo apt-get install -y clang |
| CMake | No | cmake 3.28.3 | sudo apt-get install -y cmake |
| gdb | No | gdb 15.1 | sudo apt-get install -y gdb |
| Valgrind | No | valgrind 3.22.0 | sudo apt-get install -y valgrind |
| SQLite headers | No | libsqlite3-dev 3.45.1 | sudo apt-get install -y libsqlite3-dev |
sudo works without a password inside the sandbox
(the sandbox environment).
Compile and run a program
Write the file, compile it, and run the binary in separate commands. Then a compiler error and a crash come back as different results.
TypeScriptimport { Sandbox } from "withruntime";const source = `#include <stdio.h>int main(void) { long total = 0; for (int i = 1; i <= 100; i++) total += i; printf("%ld\\n", total); return 0;}`;await using sbx = await Sandbox.create({ network: { internet: false }, timeoutSeconds: 300, onLeaseEnd: "stop",});await sbx.files.write("/workspace/main.c", source);const build = await sbx.exec(["gcc", "-O2", "-Wall", "-Wextra", "-o", "main", "main.c"]);if (build.exitCode !== 0) throw new Error(build.stderr);const run = await sbx.exec(["./main"], { timeoutMs: 10_000 });console.log(run.exitCode, run.timedOut, run.stdout); // 0 false 5050Pythonfrom withruntime import Sandboxsource = """#include <stdio.h>int main(void) { long total = 0; for (int i = 1; i <= 100; i++) total += i; printf("%ld\\n", total); return 0;}"""with Sandbox.create(network={"internet": False}, timeout_seconds=300, on_lease_end="stop") as sbx: sbx.files.write("/workspace/main.c", source) build = sbx.exec(["gcc", "-O2", "-Wall", "-Wextra", "-o", "main", "main.c"]) if build.exit_code != 0: raise SystemExit(build.stderr) run = sbx.exec(["./main"], timeout_ms=10_000) print(run.exit_code, run.timed_out, run.stdout)- An array runs the program directly, with no shell. File names and arguments from a model cannot inject commands (run commands).
- A program that never ends returns
timedOut: truewith its output so far. The default limit is 60 seconds. - A program that prints forever is capped. A result keeps at most 64 KiB of stdout and 64 KiB of stderr, and it reports what it dropped.
- gcc's messages go to stderr. Send them back to the model as they are. The model can fix its own code from them.
Catch memory bugs with sanitizers
GCC's AddressSanitizer detects out-of-bounds and use-after-free bugs. UndefinedBehaviorSanitizer detects undefined behavior at run time. Both are enabled with compiler flags (GCC instrumentation options). Nothing extra is installed:
TypeScriptimport { Sandbox } from "withruntime";await using sbx = await Sandbox.create({ network: { internet: false } });await sbx.files.write( "/workspace/bug.c", "#include <stdlib.h>\nint main(void) { int *a = malloc(4 * sizeof *a); a[4] = 1; free(a); return 0; }\n",);await sbx.exec(["gcc", "-g", "-fsanitize=address,undefined", "-o", "bug", "bug.c"], { check: true,});const run = await sbx.exec(["./bug"], { timeoutMs: 30_000 });console.log(run.exitCode, run.stderr.slice(0, 2000)); // the sanitizer's reportFor Valgrind or gdb, install them once per sandbox, or bake them into an image as shown below.
Build a Makefile project
Upload a folder and run make in it. The upload travels as one archive and
keeps file modes, so scripts stay executable.
Pythonfrom withruntime import Sandboxwith Sandbox.create(network={"internet": False}) as sbx: sbx.files.upload("./project", "/workspace/project") build = sbx.exec(["make", "-j2"], cwd="/workspace/project", timeout_ms=300_000) tests = sbx.exec(["make", "test"], cwd="/workspace/project", timeout_ms=120_000) print(build.exit_code, tests.exit_code, tests.stdout[-2000:])Start every sandbox with your toolchain
A custom image installs the extra tools once. Building it is free. A stored image is charged by its size.
TypeScriptimport { Runtime } from "withruntime";const runtime = new Runtime();await runtime.images.build({ name: "c-tools", recipe: { apt: ["clang", "cmake", "gdb", "valgrind"] },});await using sbx = await runtime.sandboxes.create({ image: "c-tools", network: { internet: false },});console.log((await sbx.exec(["valgrind", "--version"])).stdout);Terminalruntime images build --apt clang,cmake,gdb,valgrind --name c-toolsWhat it costs
Runtime bills the CPU a sandbox uses at $0.025 per vCPU-hour, and memory at $0.0075 per GiB-hour. A compile keeps a core busy for a moment. Waiting costs the floor of a twentieth of a vCPU. A 2 vCPU, 4 GiB sandbox costs $0.03125 an hour while idle, and $0.08 an hour with both cores busy (pricing). 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 -- gcc --versionFor C++, see how to run C++ code in a sandbox. To grade many submissions, see grading student code. For code a model wrote, see running untrusted LLM code.
Sources
Checked 26 October 2026.
- Ubuntu 24.04 (noble) packages: gcc-13, build-essential, clang-18, cmake, gdb, valgrind, libsqlite3-dev
- GCC instrumentation options
Facts on this page were checked on 26 October 2026.