Skip to content
Runtime

Explore many solutions in parallel with sandbox forks

Fork one prepared sandbox into up to 100 copies. Run a different agent attempt in each copy, score them the same way, and keep the best.

On Runtime, one fork call returns up to 100 running copies of a prepared machine. Memory and running processes are included. The snapshot that the call takes for itself costs nothing. The source pauses while the fork is taken. It runs again when the call returns. Each copy is billed like a new sandbox of its size: $0.03125 an hour while the agent waits on its model, at 2 vCPU and 4 GiB. These are Runtime's rates as published on 26 October 2026 (pricing).

Why explore in parallel

A single agent run is one sample from a distribution. Ask again with another plan, another prompt or another model, and you get a different answer. Some of those answers are better than others. Scoring several attempts and keeping the best raises the odds that at least one succeeds. This works when you can do two things cheaply:

  • Give every attempt the same starting point. Otherwise a better score may come from a luckier environment, not a better solution.
  • Score every attempt in the same objective way, with tests, a benchmark, a type checker or a rubric, not by asking the agent whether it succeeded.

A fork provides the first. Your scorer runs inside each copy to provide the second.

Parallel exploration versus other patterns

Pattern Branches from Suits Page
Best of N (this page) One prepared state A task with a clear score: tests, speed, size Here
Tree search Every promising step Long tasks where early choices matter Tree search with forks
pass@k evaluation A task image Measuring a model, not shipping a fix Agent evals
Candidate filtering An installed project Keeping only generated artifacts that pass Test generation

The core loop

The example asks an agent to make a slow function faster. Each copy gets a different strategy. The scorer runs the tests and a benchmark. The fastest correct copy wins.

TypeScriptimport { Sandbox } from "withruntime";const strategies = [  "Vectorise the inner loop with NumPy.",  "Cache repeated lookups; change nothing else.",  "Rewrite the parser as a single pass.",  "Profile first, then fix the top hotspot only.",];// Your agent loop: sandbox tools bound to `copy`, the strategy in the prompt.async function runAgent(copy: Sandbox, strategy: string) {  await copy.files.write("/workspace/strategy.txt", strategy);}await using base = await Sandbox.create({ diskMiB: 8192, timeoutSeconds: 3600 });await base.exec("git clone --depth 1 https://github.com/your-org/your-lib.git repo", {  check: true,  timeoutMs: 300_000,});await base.exec("cd repo && pip install -e . pytest", { check: true, timeoutMs: 600_000 });const copies = await base.fork({ count: strategies.length, labels: { task: "speed-up-parse" } });const scored = await Promise.all(  copies.map(async (copy, i) => {    await runAgent(copy, strategies[i]!);    const tests = await copy.exec("python3 -m pytest -q", {      cwd: "/workspace/repo",      timeoutMs: 900_000,    });    const bench = await copy.exec("python3 bench.py", {      cwd: "/workspace/repo",      timeoutMs: 300_000,    });    const seconds = tests.exitCode === 0 ? Number(bench.stdout.trim()) : Infinity;    return { copy, strategy: strategies[i]!, seconds };  }),);scored.sort((a, b) => a.seconds - b.seconds);const best = scored[0]!;const diff = await best.copy.exec("git diff", { cwd: "/workspace/repo" });console.log(best.strategy, best.seconds, diff.stdout.length);await Promise.all(scored.map((s) => s.copy.stop()));
Pythonfrom concurrent.futures import ThreadPoolExecutorfrom withruntime import Sandboxstrategies = [    "Vectorise the inner loop with NumPy.",    "Cache repeated lookups; change nothing else.",    "Rewrite the parser as a single pass.",    "Profile first, then fix the top hotspot only.",]def run_agent(copy, strategy):    """Your agent loop: sandbox tools bound to copy, the strategy in the prompt."""    copy.files.write("/workspace/strategy.txt", strategy)def attempt(pair):    copy, strategy = pair    run_agent(copy, strategy)    tests = copy.exec("python3 -m pytest -q", cwd="/workspace/repo", timeout_ms=900_000)    bench = copy.exec("python3 bench.py", cwd="/workspace/repo", timeout_ms=300_000)    seconds = float(bench.stdout.strip()) if tests.exit_code == 0 else float("inf")    return {"copy": copy, "strategy": strategy, "seconds": seconds}with Sandbox.create(disk_mib=8192, timeout_seconds=3600) as base:    base.exec("git clone --depth 1 https://github.com/your-org/your-lib.git repo",              check=True, timeout_ms=300_000)    base.exec("cd repo && pip install -e . pytest", check=True, timeout_ms=600_000)    copies = base.fork(count=len(strategies), labels={"task": "speed-up-parse"})    with ThreadPoolExecutor(max_workers=len(copies)) as pool:        scored = sorted(pool.map(attempt, zip(copies, strategies)), key=lambda s: s["seconds"])    best = scored[0]    print(best["strategy"], best["seconds"], len(best["copy"].exec("git diff", cwd="/workspace/repo").stdout))    for s in scored:        s["copy"].stop()

The copies run at the same time. The total wall-clock time is roughly that of the slowest attempt.

What varies between attempts

Any input to your agent can be the axis:

  • Plans, as above. Each copy gets one strategy, written by a person or proposed by a planning model.
  • Models: the same prompt sent to two or three providers, scored on equal terms.
  • Temperatures or seeds: the same model, sampled several times.
  • Tool budgets: a short run and a long run, to find out whether more steps help this kind of task.

Record the axis in the copy's labels. Later, runtime.sandboxes.list({ labels }) finds every copy of one exploration. Lifecycle events for each copy are kept for 14 days (metrics and webhooks).

Past 100 copies

fork makes 1 to 100 copies per call. For more copies, keep the prepared machine as a snapshot with base.snapshot({ name, retentionDays }). Then create sandboxes from it by passing snapshot: id. The account limits how many sandboxes run at the same time: 100 on a paid account. Creates beyond that limit wait for room. An account without credit runs four sandboxes at the same time. On such an account, a fork of three copies, plus the source, is the most that fits. A kept snapshot is billed as storage at $0.08 per decimal GB per 30-day month. The fork's own snapshot is deleted when the fork ends (snapshots and forks).

What parallel exploration needs

Need How Runtime covers it
An identical start for every attempt Forks copy files, memory and running processes
Attempts that cannot interfere Each copy is its own Firecracker microVM with its own kernel and disk
Fast branching About a second for a fresh sandbox. The time grows with the memory it holds
Cheap waiting on models Billed on measured CPU: $0.03125 an hour when idle, at 2 vCPU, 4 GiB
A copy that fails to start details.startedSandboxIds lists the copies that started; stop them
Knowing which attempt was which Labels on each copy; runtime.sandboxes.list({ labels })

What it costs

Take 100 tasks a month. Each task is explored with ten copies. Each copy runs for 15 minutes on 2 vCPUs and 4 GiB. Its builds, tests and benchmark use 180 CPU-seconds:

CPU
1,000 × 180 s / 3,600 × $0.025
$1.25
Memory
1,000 × 900 s / 3,600 × 4 GiB × $0.0075
$7.50
Total
$8.75

Each task costs about 9 cents for ten attempts. This does not include the prepared base, which is one more sandbox per task (pricing). Your model provider bills model calls separately. Every account gets 100 free hours of a 1 GB machine in its first 30 days, with no card.

Related: tree search with forks, what a sandbox fork is, coding agent sandbox, dependency upgrades, RL environments.

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