Skip to content
Runtime

How to have an AI agent set up any repository in a sandbox

Clone the repository into a fresh microVM. Let the agent install and test until the suite passes. Then snapshot the machine for later tasks.

On Runtime, a setup that worked once becomes the starting point of every later task. A snapshot keeps the sandbox's files, memory and running processes while you have credit. The next task starts with dependencies installed, services running and the test suite known to pass. Onboarding 40 repositories a month costs $7.20 at Runtime's rates, snapshots included. The calculation is below (pricing, 26 October 2026).

What "onboarding a repository" means for an agent

A person new to a codebase reads the README, installs the toolchain, works through the one dependency that needs a system library, and runs the tests until they pass. An agent does the same work, faster and less carefully. Two things make it safe and useful:

  • A machine of its own. The agent runs curl | sh installers, sudo apt-get, and whatever the README says. None of it should touch your laptop or CI runners.
  • A record of what worked. The commands that led to a passing test run are the setup script that nobody wrote down. Keep those commands, and keep the machine.

Every sandbox has Ubuntu 24.04 with Python 3.12, Node.js 24, Bun, git and gcc. sudo works, so most projects need only their own dependencies (the sandbox environment).

The core loop

Clone the repository and show it to the agent. Run the commands the agent chooses. Stop when it says the tests pass. Keep every command, then snapshot the result:

TypeScriptimport { Runtime } from "withruntime";// Your model: given everything so far, the next shell command, or null when the tests pass.type NextCommand = (transcript: string) => Promise<string | null>;const runtime = new Runtime();export async function onboard(repoUrl: string, next: NextCommand, maxSteps = 30) {  const sbx = await runtime.sandboxes.create({    diskMiB: 16_384,    timeoutSeconds: 3600,    labels: { job: "onboard" },  });  await sbx.exec(["git", "clone", "--depth", "1", repoUrl, "/workspace/repo"], {    check: true,    timeoutMs: 300_000,  });  const look = await sbx.exec("ls -a; head -c 6000 README* 2>/dev/null", {    cwd: "/workspace/repo",  });  let transcript = look.stdout;  const steps: string[] = [];  for (let i = 0; i < maxSteps; i++) {    const command = await next(transcript);    if (command === null) break;    const run = await sbx.exec(command, { cwd: "/workspace/repo", timeoutMs: 900_000 });    if (run.exitCode === 0) steps.push(command);    const tail = (run.stdout + run.stderr).slice(-4000);    transcript += `\n$ ${command}\nexit ${run.exitCode}${run.timedOut ? " (timed out)" : ""}\n${tail}`;  }  await sbx.files.write(    "/workspace/setup.sh",    `set -e\ncd /workspace/repo\n${steps.join("\n")}\n`,    {      mode: 0o755,    },  );  const snapshot = await sbx.snapshot({ name: "repo-ready", retentionDays: 30 });  await sbx.stop();  return { snapshotId: snapshot.id, steps };}
Pythonfrom withruntime import Runtimeruntime = Runtime()def onboard(repo_url: str, next_command, max_steps: int = 30) -> dict:    """next_command(transcript) returns the next shell command, or None when the tests pass."""    sbx = runtime.sandboxes.create(disk_mib=16_384, timeout_seconds=3600, labels={"job": "onboard"})    sbx.exec(["git", "clone", "--depth", "1", repo_url, "/workspace/repo"], check=True, timeout_ms=300_000)    transcript = sbx.exec("ls -a; head -c 6000 README* 2>/dev/null", cwd="/workspace/repo").stdout    steps = []    for _ in range(max_steps):        command = next_command(transcript)        if command is None:            break        run = sbx.exec(command, cwd="/workspace/repo", timeout_ms=900_000)        if run.exit_code == 0:            steps.append(command)        tail = (run.stdout + run.stderr)[-4000:]        transcript += f"\n$ {command}\nexit {run.exit_code}{' (timed out)' if run.timed_out else ''}\n{tail}"    sbx.files.write("/workspace/setup.sh", "set -e\ncd /workspace/repo\n" + "\n".join(steps) + "\n", mode=0o755)    snapshot = sbx.snapshot(name="repo-ready", retention_days=30)    sbx.stop()    return {"snapshot_id": snapshot["id"], "steps": steps}
  • The clone uses an array command, so a repository URL cannot carry shell syntax. For a private repository, upload your local checkout with sbx.files.upload("./repo", "/workspace/repo") instead. No token then enters the sandbox.
  • A command that runs longer than 60 seconds streams its output. Its result keeps everything it printed. The loop keeps only the end of the output, so a noisy install cannot flood the model's context.
  • Only commands that exit with code 0 go into setup.sh. Review the file before you run it anywhere else.

Start every later task from the working machine

The snapshot holds the repository, installed and tested. A coding task, an eval or a review creates a sandbox from it:

TypeScriptimport { Runtime } from "withruntime";const runtime = new Runtime();export async function taskFrom(snapshotId: string, testCommand: string) {  await using sbx = await runtime.sandboxes.create({    snapshot: snapshotId,    labels: { job: "task" },  });  await sbx.exec("git pull --ff-only", { cwd: "/workspace/repo", timeoutMs: 120_000 });  const tests = await sbx.exec(testCommand, { cwd: "/workspace/repo", timeoutMs: 900_000 });  return tests.exitCode === 0; // still green? then hand the sandbox to the agent}

Several agents at the same time? fork({ count }) copies a running sandbox into 1 to 100 running sandboxes that it returns together. Each one has its own disk (sandbox forks).

Snapshot or image?

Keep it as What it holds Best when Charged
A snapshot Files, memory and running processes Services should be up the moment a task starts $0.08/GB/month on bytes it alone stores
A custom image Files, rebuilt from a recipe or Dockerfile The setup should be reviewed and versioned $0.08/GB/month on its accounted storage
Neither Nothing One-off tasks on small repositories Only the running time

setup.sh is the bridge between the two. Once reviewed, its commands become the RUN lines of a Dockerfile. runtime.images.build turns that Dockerfile into a numbered image version (custom images).

What repository onboarding needs

Need How Runtime covers it
Freedom to install anything sudo works inside the guest; root cannot change network, CPU, memory or cost
Room for dependencies diskMiB up to what the project needs; the default 4 GiB has about 2.5 free
Databases the tests expect sudo enable-docker, then Docker Compose in the same sandbox
Long installs timeoutMs up to 24 hours per command
Keeping what worked Snapshots kept while you have credit; setup.sh from the commands that exited 0
Several repositories at the same time 100 sandboxes at the same time on a paid account
Your code kept from other tenants A Firecracker microVM with its own kernel per sandbox

What it costs

Take 40 repositories a month. Each onboarding keeps a 2 vCPU, 4 GiB sandbox running for 20 minutes. Installs and tests use 1.2 vCPUs on average. Each snapshot keeps 2 GB of its own for the month:

CPU
40 × 20 min / 60 × 1.2 vCPU × $0.025
$0.40
Memory
40 × 20 min / 60 × 4 GiB × $0.0075
$0.40
Snapshots
40 × 2 GB × $0.08
$6.40
Total
$7.20

A snapshot is charged on the bytes it alone stores. A block that two of your snapshots share counts once (pricing). A new account gets 100 free hours of a 1 GB machine in its first 30 days, with no card. Without credit, a sandbox can reach only ports 80 and 443, so clone over HTTPS in that case.

Start

Terminalnpx withruntime sandboxes run --keep -- git --version

The first run prints a link to approve in your browser. runtime sandboxes ssh <id> then opens a shell in the sandbox that the agent set up (SSH and editors).

Related: a coding agent sandbox, legacy code migration, sandbox snapshots, per-user dev environments, agent evals and SWE-bench.

A stored image is charged for its whole file (image storage pricing).

Facts on this page were checked on 26 October 2026.

Related.

100 free hoursin your first 30 days.

of a 1 GB machine

  • No credit card
  • Four sandboxes at once, 2 vCPU and 4 GiB each
  • Then prepaid credit from $10, no plan fee
Start free, no card
Start free, no card