Sandbox for a coding agent: give an AI agent its own computer
Give the agent a Linux microVM with the repository cloned, let it run commands and tests there, and take back the diff when it is done.
On Runtime a coding agent's machine costs $0.03125 an hour while the agent waits on its model, and $0.08 an hour with both CPUs busy, for 2 vCPUs and 4 GiB. Runtime bills the CPU the agent's commands use, not the CPUs it holds, which made it 42% to 88% cheaper than eleven other sandbox providers on an agent job that mostly waits, at rates checked 23 September 2026 (the comparison).
The short answer
Create a sandbox, clone the repository, hand the model four tools bound to that sandbox, and read the diff at the end.
TypeScriptimport { Sandbox } from "withruntime";import { sandboxTools } from "withruntime/tools";const repo = "https://github.com/your-org/your-repo.git";await using sbx = await Sandbox.create({ diskMiB: 8192, timeoutSeconds: 3600, labels: { agent: "coder", task: "1234" }, network: { internet: true, allow: ["github.com", "*.github.com", "registry.npmjs.org"] },});await sbx.exec(["git", "clone", "--depth", "1", repo, "repo"], { check: true, timeoutMs: 300_000 });await sbx.exec("cd repo && npm ci", { check: true, timeoutMs: 600_000 });// runtime_exec, runtime_read_file, runtime_write_file, runtime_list_files:// a name, a description, a JSON Schema and a function each, for any tool format.const tools = sandboxTools(sbx);console.log(tools.map((t) => t.name));// ... your model's tool-calling loop runs here ...const diff = await sbx.exec("git diff", { cwd: "/workspace/repo" });const tests = await sbx.exec("npm test", { cwd: "/workspace/repo", timeoutMs: 900_000 });console.log(tests.exitCode, diff.stdout);Pythonfrom withruntime import Sandboxfrom withruntime.tools import sandbox_toolsrepo = "https://github.com/your-org/your-repo.git"with Sandbox.create( disk_mib=8192, timeout_seconds=3600, labels={"agent": "coder", "task": "1234"}, network={"internet": True, "allow": ["github.com", "*.github.com", "pypi.org", "*.pythonhosted.org"]},) as sbx: sbx.exec(["git", "clone", "--depth", "1", repo, "repo"], check=True, timeout_ms=300_000) sbx.exec("cd repo && pip install -e .", check=True, timeout_ms=600_000) run, read, write, ls = sandbox_tools(sbx) # give these to your agent framework # ... your model's tool-calling loop runs here ... diff = sbx.exec("git diff", cwd="/workspace/repo") tests = sbx.exec("python3 -m pytest -q", cwd="/workspace/repo", timeout_ms=900_000) print(tests.exit_code, diff.stdout)The model chooses commands and paths, never the sandbox or the account. The tools cap their output, so one noisy command cannot flood the model's context. The same four tools come ready for the OpenAI Agents SDK, Vercel AI SDK, Claude Agent SDK, LangChain, Mastra and five more frameworks (frameworks).
What a coding agent needs from its sandbox
| Need | How Runtime covers it |
|---|---|
| A real Linux machine | Ubuntu 24.04 with Python 3.12, Node.js 24, Bun, git, gcc and sudo |
| Isolation from your laptop and CI | A Firecracker microVM with its own kernel, on servers Runtime operates |
| Dependencies ready at start | A custom image from a recipe, any container image or a Dockerfile |
| Services the tests need | sudo enable-docker, then docker compose up -d |
| A dev server to look at | A private HTTPS preview of any port |
| Push access without leaking a token | A secret the sandbox only sees as a placeholder |
| Long tasks | Commands up to 24 hours; leases up to an hour, extended as often as needed |
| Waiting on review | Pause keeps files, memory and running processes for 1 to 365 days |
| Several attempts at one task | fork makes 1 to 10 running copies of the prepared machine |
| An agent that retries | Every write carries an idempotency key, so a retry never starts a second sandbox |
| An agent that spends | A daily spending limit per key, which only a person can set |
Try several approaches at once
Set the repository up once, then fork it. Each copy starts from exactly that point, with the dependencies installed and any server still running, and the agent tries a different plan in each:
TypeScriptimport { Sandbox } from "withruntime";await using base = await Sandbox.create({ diskMiB: 8192 });await base.exec("git clone --depth 1 https://github.com/your-org/your-repo.git repo", { check: true, timeoutMs: 300_000,});const attempts = await base.fork({ count: 3 });const results = await Promise.all( attempts.map((copy) => copy.exec("cd repo && npm test", { timeoutMs: 900_000 })),);console.log(results.map((r) => r.exitCode));await Promise.all(attempts.map((copy) => copy.stop()));Pythonfrom withruntime import Sandboxwith Sandbox.create(disk_mib=8192) as base: base.exec("git clone --depth 1 https://github.com/your-org/your-repo.git repo", check=True, timeout_ms=300_000) attempts = base.fork(count=3) results = [copy.exec("cd repo && npm test", timeout_ms=900_000) for copy in attempts] print([r.exit_code for r in results]) for copy in attempts: copy.stop()The source pauses for the moment the fork takes, about a second for a fresh sandbox, then carries on (sandbox forks).
Push a branch without handing over the token
Store the GitHub token as a Runtime secret. Each sandbox gets a placeholder; the
host's proxy puts the real value into HTTPS requests to the hosts you name, and
nowhere else, so a prompt injection cannot read it. git push over HTTPS goes
to github.com with Basic credentials, the user name x-access-token and the
token as the password, so the secret holds that pair:
TypeScriptimport { Runtime } from "withruntime";const runtime = new Runtime();const token = process.env.GITHUB_TOKEN ?? ""; // a fine-grained token for this repositoryawait runtime.secrets.set("GIT_GITHUB_AUTH", { value: Buffer.from(`x-access-token:${token}`).toString("base64"), hosts: ["github.com"], header: "Authorization", format: "Basic {value}",});await using sbx = await runtime.sandboxes.create();await sbx.exec("git clone https://github.com/acme/app.git app", { check: true, timeoutMs: 300_000,});// ...the agent works on a branch...await sbx.exec("cd app && git push origin agent/fix-parser", { check: true, timeoutMs: 120_000 });The URL carries no credentials and git needs no helper: the proxy sets the
header on every HTTPS request to github.com. To open the pull request through
GitHub's API as well, store the token a second time for api.github.com with
the format token {value}
(secrets sandboxes never see).
Git in a sandbox covers SSH deploy keys and faster clones.
Coding agents that run on their own
Claude Code, Codex, Cursor and other MCP clients can drive Runtime sandboxes through Runtime's MCP server, approved once in the browser with no key to copy:
Terminalclaude mcp add --scope user runtime -- npx -y withruntime mcpcodex mcp add runtime -- npx -y withruntime mcpTo run the coding agent itself inside a sandbox, see Claude Code and Codex.
What it costs
Take 1,000 agent tasks a month. Each keeps a 2 vCPU, 4 GiB sandbox running for 30 minutes, and its commands (installs, builds, tests) use 5 CPU-minutes, 300 CPU-seconds, while the rest is spent waiting on the model:
TextCPU: 1,000 × 300 s / 3,600 × $0.025 = $2.08Memory: 1,000 × 1,800 s / 3,600 × 4 GiB × $0.0075 = $15.00Total: $17.08That is under 2 cents a task, from Runtime's rates of $0.025 per vCPU-hour of measured CPU and $0.0075 per GiB-hour (pricing). A paused sandbox waiting on review costs no compute; it is billed as paused storage at $0.08 per GB per 30-day month. New accounts get 50 free sandbox hours, no card.
Start
Terminalnpx withruntime sandbox run --trial -- git --versionThe first run prints a link to approve in your browser. Then use the JavaScript or Python SDK as above.
Related: run untrusted LLM code safely, preview apps an agent builds, run Docker in a sandbox, what an agent sandbox is.
Facts on this page were checked on 25 September 2026.