Runtime

How to compile and run C code in a sandbox

Write the source into a Linux microVM, compile it with gcc there, and run the binary with a time limit and the internet off.

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, so a C file compiles in the first command after create. A new sandbox took 351 ms from the create request to its first Python result at the median, measured on 24 September 2026 (speed), which makes a fresh machine per 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, with CPU, memory, network and cost 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

Tool In the default image Ubuntu 24.04 package, checked 25 September 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.0.50 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 two separate commands, so 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 5050
Pythonfrom 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, so file names and arguments from a model cannot inject commands (run commands).
  • A program that never ends returns timedOut: true with 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 says what it dropped.
  • gcc's messages go to stderr. Send them back to the model as they are; it can fix its own code from them.

Catch memory bugs with sanitizers

GCC's AddressSanitizer detects out-of-bounds and use-after-free bugs, and UndefinedBehaviorSanitizer detects undefined behaviour at run time, both from 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 report

For Valgrind or gdb, install them once per sandbox, or bake them into an image as 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 on 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 image build --apt clang,cmake,gdb,valgrind --name c-tools

What 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 idle and $0.08 an hour with both cores busy (pricing). New accounts get 50 free sandbox hours, no card:

Terminalnpx withruntime sandbox run --trial -- gcc --version

For C++, see how to run C++ code in a sandbox. To grade many submissions, see grading student code, and for code a model wrote, running untrusted LLM code.

Sources

Checked 25 September 2026.

Facts on this page were checked on 25 September 2026.