Common Methodology
Defines the test environment, trial lifecycle, timing clocks, failure classifications, statistical estimators, and independence checks shared across all BoxRouter benchmarks.
All providers are benchmarked through BoxRouter's unified sandbox API, which standardizes operation behavior across providers. See Provider support for the guarantees.
Test Environment
Scope: BoxRouter benchmarks measure how fast cloud sandboxes run and what they cost. The tests cover coding agents, reinforcement learning rollouts, continuous integration pipelines, sandbox operations, and machine performance.
Accounts: Every provider is tested the way an ordinary customer would use it. Each benchmark runs through BoxRouter Core and pinned provider adapters against a self-serve account, with no provider-specific tuning.
Region: Every provider runs in its default US region. Boat is the exception: it has no US region and no region selector, so its sandboxes run in the EU, and its results are labeled with that region. Boat's results on metrics timed from the US job host carry a marker and a note, because they include the transatlantic network round trip.
Sandbox Size: Each benchmark requests a 2 vCPU / 4 GiB sandbox unless its methodology page specifies another size, and records the CPU count and memory the sandbox reports.
Images: Every image is pinned by digest and prepared in each provider account before the benchmark run starts. Standard benchmarks use the BoxRouter standard image, pinned by its SHA-256 digest (ghcr.io/boxrouter/standard-image@sha256); a changed digest starts a separate results series.
DeepSWE, Terminal-Bench 4.0, and R2E-Gym need custom toolchains, so they use task-specific images, also pinned by digest. The Create tests append a unique per-trial layer to a pinned base image.
Job Host: Every provider is timed from its own dedicated cloud job host. The job hosts are machines of the same type in the AWS region the publication names, and every trial record stores the host it ran from. Results from different job-host regions are never combined.
Network Path: Every operation timed on the job host includes the network path between the job host and the provider's region. Before a provider's benchmark chain starts, the benchmark script records a baseline for that path: the median time to open a TCP connection to each of the provider's API endpoints, over 10 attempts.
The baseline is untimed, creates no sandbox, makes no API call, and is stored in the run manifest with the job host's name.
Connections: Calls reuse a warm connection. Where the provider's SDK supports connection keep-alive, idle client connections stay open for up to 5 min, so sequential calls reuse a warm TLS session. Daytona's SDK exposes no idle-timeout setting: its command channel keeps its connection open, and its file-transfer channel reconnects after 5 s of idle time.
Eligibility: A provider's capability declaration decides which benchmarks it enters. The declarations live in https://github.com/boxrouter/sandbox-provider-spec, in the files providers/<provider>/declaration*.yaml. A benchmark includes a provider when the declaration lists every capability the benchmark requires, and excludes the provider when a required capability is absent.
- Image Digest
-
sha256
- Image Version
-
0.3.11
- Image Registry Page
-
View on the Registry
- Pull the Image
-
docker pull ghcr.io/boxrouter/standard-image@sha256:89d4c97048dbdfb8b6d85ad91833962ff38d1c334a5f47e6de80a9762310191b Copy
Trial Lifecycle
Steps: Every trial follows the same four steps.
- Sandbox creation: A fresh sandbox starts with the test environment's machine size and image, unless the benchmark page specifies others.
- Readiness check: An untimed check waits until the sandbox accepts commands, as Readiness Check below describes.
- Timed procedure: The benchmark's measured steps run. The first failed step ends the trial, with no retries inside it.
- Resource cleanup: Every sandbox, snapshot, and temporary image the trial created is deleted and confirmed gone. Each sandbox carries a batch label and a provider-enforced lifetime, so a crashed trial cannot leave one running. R2E-Gym counts
destroy in its charted total; other benchmarks record it without charting it.
Time Limits: Every step has a time limit above the slowest normal time seen on any provider, so only a hung call reaches it. Each benchmark lists its limits in its Failures section.
Long Commands: A command that can run for tens of seconds or more (a data load, a fio run, an install, a replay script) runs in the background, because a provider's gateway can end a synchronous call that waits too long. The benchmark script polls it every 10 s until it exits; its start and end come from the sandbox's clock, so polling adds nothing to the measured runtime.
Readiness Check
Purpose: Some providers return from create before the sandbox accepts commands, so the first requests fail with HTTP 502, HTTP 503, HTTP 504, connection refused, or connection reset until it does.
Backoff: Right after create returns, the benchmark script sends a trivial command (true) through BoxRouter, with a 30 s timeout per attempt and no guest clock wrapper, retried with exponential backoff: first wait 20 ms, ×1.5 per not-ready answer, capped at 200 ms, ±20% jitter, no attempt more than 10 s after the first.
Deadline: A check that reaches its deadline without a ready answer, or fails with any other error, ends the trial as a provider failure. Every attempt and the time to interactive are kept in private evidence.
Timed Readiness: For some tests the wait for a ready sandbox is part of the timed measurement, so no untimed check runs before it. Sequential TTI sends its first timed command as soon as create returns. On a sandbox restored from a snapshot, Snapshot and Restore, Fork and R2E-Gym send the first timed step as soon as restore returns; the source sandbox made by create still gets the check.
Timing and Clocks
Clocks: Every timed span starts and ends on the same machine's clock. A timed span contains only the steps its benchmark names, with no warm-up request, probe, or keep-alive inside it. Each span uses one of two clocks:
- Job-host clock: The job host's monotonic clock times control-plane and lifecycle operations. BoxRouter Core times each operation (
create, snapshots.create, restore, attach, file transfers, and destroy) from adapter entry, after payload preparation, until the call returns or raises an exception. Post-create readiness checks, output archive downloads, and cleanup verification are excluded. Lifecycle and file-transfer operations run without a guest shell wrapper. - Sandbox clock: The guest's own clock times commands inside the sandbox. Network transport between the job host and the provider therefore does not inflate CPU, memory, or disk measurements.
Latency: The caller's wait (total time) splits into the command's execution inside the sandbox (runtime) and the transport overhead around it, which BoxRouter calls "latency" (total time minus runtime):
t_{\mathrm{latency}} = t_{\mathrm{total}} - t_{\mathrm{runtime}}
-
t_{\mathrm{latency}} Latency: the difference of the total time and the runtime of a timed span. -
t_{\mathrm{total}} Total time: the operation duration of a timed span, measured on the job host. -
t_{\mathrm{runtime}} Runtime: the command duration of a timed span, measured on the sandbox's own clock.
Guest Timing and the Clock Gate
Clock Wrapper: A short Bash clock wrapper times guest commands without starting a subprocess. Bash's $EPOCHREALTIME builtin records microsecond wall-clock timestamps before and after the command. The wrapper writes the result as a __SANDBOX_CLOCK trailer line on standard error.
Monotonic Clock: A second, monotonic clock catches guest clocks that jump, since virtualized guest wall clocks and restored snapshots can step when the host synchronizes them. The wrapper also reads /proc/uptime, a monotonic kernel counter with 10 ms resolution, into a shell variable before and after the timed span.
It reads the whole file with the read -r -N 64 builtin, because virtualized procfs implementations such as Sysbox can fail on subsequent line reads.
Clock Gate: A guest runtime counts only when both clocks agree. The clock gate accepts a runtime when the wall-clock span and the uptime span satisfy:
\Delta t_{\mathrm{wall}} \ge 0, \Delta t_{\mathrm{up}} \ge 0, \text{and} \left| \Delta t_{\mathrm{wall}} - \Delta t_{\mathrm{up}} \right| \le t_{\mathrm{tol}}
-
\Delta t_{\mathrm{wall}} Elapsed time of the timed span from the start s and end e $EPOCHREALTIME readings: e - s. -
\Delta t_{\mathrm{up}} Elapsed /proc/uptime duration over the same span. -
t_{\mathrm{tol}} The clock gate's tolerance, given below.
If the two spans differ by more than 20 ms, or either span is negative, the trial's runtime is discarded as a stepped clock rather than estimated.
Failures, Exclusions, and Top-Ups
Published Results: Published speed and cost results come from successful trials only. Unsuccessful attempts, their ownership, replacement links, and billed costs stay in private evidence for cleanup verification and diagnosis. Provider failure rates are measured separately, in the Reliability benchmark.
Outcomes: Every trial ends in one of these outcomes.
- Success: The trial did everything it had to. It passed every step and post-run integrity check within its time limit, held the clock gate on every guest-timed span, and had every sandbox, snapshot, and temporary image it created confirmed deleted.
- Provider failure: The provider or its sandbox failed. This covers any
create, snapshot, restore, destroy, or transport error; a sandbox that stops answering; a sandbox whose stop can't be confirmed; a readiness check that reaches its 10 s deadline without a ready answer or fails with another error; a replay script that ends early without writing a command-mismatch record; and any failure whose owner was not recorded. - Benchmark error: BoxRouter's own benchmark script, adapter binding, or core software failed, for example with output it cannot parse after a command succeeded. The error stops that unit on that provider for the rest of the run instead of retrying it in top-up rounds. The defect is fixed and the unit is re-run in a later run, without charging the failure to the provider.
- Excluded trial: A deterministic trace or live-registry CI job diverged on the provider ("does not replay here"). It diverges when a command returns an unexpected exit code, a final git patch's SHA-256 digest mismatches, an upstream verifier does not write a passing reward after clean command execution, an upstream package registry fails, or a step hits its timeout (exit code 124) while the sandbox still answers a post-timeout check. Excluded trials do not count against the provider, because a live agent would see the output and take another path.
- Missed trial: A pre-test environment check skipped the trial before the measured procedure ran, for example because the sandbox's
/workspace rejects O_DIRECT or has less free disk space than Disk or Hard Links requires. A missed trial is neither a success nor a failure.
Status Labels: A provider without a ranked bar shows a status label instead. When a provider has no ranked bar, or has unresolved failures on a chart, the result shows one of these status labels:
| Status | Meaning |
| Not Ranked | The workload is not ranked because it has not reached its declared number of successful trials. |
| Provider Failures | The provider has failed trials that top-up rounds did not replace. |
| Not Offered | This option is not offered by the provider on its plan. |
| Account Limit | Above the concurrency limit of BoxRouter's account with this provider. |
| Not Supported | BoxRouter does not run this benchmark on this provider yet: a capability it needs is not supported through BoxRouter, or a known fault holds the test back. |
| Not Measured | No result was measured for this provider. |
| Not Displayed | The provider is left off this view: it has no result here, or it lacks an input the view needs, such as a provider-sourced cost. |
Top-Ups
Refills: Each benchmark refills lost trials until every provider reaches its target. The target is the scheduled number of successful trials, N. Missed, excluded, and provider-failed slots are refilled within the run budget, and each failed trial stays in the data with its owner and cause. Independent trials are topped up one at a time, while interacting trials need a complete rerun.
Stop Conditions: Refilling stops when any of these conditions holds:
- Two refill rounds in a row add no successful trial.
- More re-runs than the intended number of successes N would be needed, so a unit never takes more than 2N trial attempts.
- The refill time limit expires.
- The run budget is spent.
- A failure cannot be replaced: the account's spend limit was reached, a trial was cut short so its outcome is unknown, resource cleanup could not be verified, or a batch stopped early.
Unknown Outcomes: An unknown owner and an unknown outcome are handled differently. A failure whose owner was not recorded counts as a provider failure and is refilled. A trial cut short with an unknown outcome stops refilling.
Statistics
Trials Used: Statistics are computed per provider and per unit, over successful trials only. No trial is trimmed unless the benchmark's methodology page states an exception. Cost aggregation and cost confidence intervals follow Pricing Methodology.
Ranking Floor: A provider enters a ranking only after enough successful trials. Each benchmark sets that minimum as its ranking floor, which its methodology page states. On multi-unit workloads (DeepSWE, Terminal-Bench 4.0, R2E-Gym, and CI Actions), the floor counts successes across all units, and a provider is ranked on the All view only once it also reports a provider-sourced cost.
Per-Unit Statistics
Percentiles: Percentiles use the nearest-rank method over the unit's n successful trials, sorted in ascending order:
Q_p = x_{(r)}, r = \max\left( 1,\ \left\lceil \dfrac{p}{100} \times n \right\rceil \right)
-
Q_p The nearest-rank percentile p of a unit's successful trials. -
x_{(r)} The value at rank r of the unit's n successful trials sorted in ascending order. -
r The rank. -
p The percentile, from 0 to 100. -
n The number of successful trials of the unit.
Median: The median is the headline statistic. It is the smallest trial value with at least half of the successful trials at or below it.
Tail: The tail statistic reports the worse side of the spread. p95 is the 95th percentile where lower is better, and the fifth percentile where higher is better.
Reporting p95 requires at least 20 successful trials for the unit. Below that, the single worst successful trial is reported instead, labeled Slowest for time metrics or Lowest for throughput and rate metrics.
Mean: The arithmetic mean is a secondary statistic over all n untrimmed successful trials:
x_{\mathrm{mean}} = \dfrac{x_1 + \cdots + x_n}{n}
-
x_{\mathrm{mean}} The arithmetic mean of a unit's successful trial values. -
x_1 The first successful trial's value. -
x_n The last successful trial's value. -
n The number of successful trials of the unit.
Confidence Interval: The time median carries a 95% percentile-bootstrap confidence interval. The bootstrap draws n values with replacement from the unit's n successful trials 10000 times, with the random seed fixed at 0, and the interval is the central 95% of the resampled medians.
Pricing Methodology defines the bootstrap interval for mean cost per success.
Pooled Trials: A benchmark that pools several calls per trial resamples whole trials. Latency per Call takes many calls from each sandbox, and calls inside one sandbox are not independent, so the bootstrap resamples whole trials with replacement rather than individual calls.
Multi-Unit Workload Value
Geometric Mean: A workload's value must not let its longest task dominate. Workloads such as DeepSWE, Terminal-Bench 4.0, R2E-Gym, and CI Actions contain units whose durations span an order of magnitude, so a sum or an arithmetic mean would let the longest task decide the ranking. On the All view, we aggregate each unit's selected statistic using the geometric mean, the workload value.
T_p = \left( \prod_{u \in U} m_{p,u} \right)^{1/|U|}
-
T_p Workload value of provider p. -
m_{p,u} Provider p's value on unit u under the selected statistic. -
U The set of units completed by at least one provider; |U| is the number of units in it.
Equal Weight: Every unit carries equal proportional weight. Making a short unit ten percent faster changes the workload value by the same percentage as making a long unit ten percent faster.
Missing Units: A provider cannot improve its value by failing a unit. A geometric mean needs positive values over the same unit set for every compared provider, and dropping a unit where a provider failed would let that provider improve its workload value by failing its slowest task.
So when a provider completes at least one unit of a workload but has no successful trials on a unit, its value on that unit is filled with the slowest time (and highest cost) observed on the unit across all providers that completed it, and the result carries a substitution note.
Overview Composites: On Overview, Composite Workload Time and Composite Workload Cost take a second unweighted geometric mean across the workload values in the selected scope.
Stage Segments and Derived Views
Stage Medians: Each stage in a stacked bar has its own median. When a benchmark splits total time into stages (such as Read, Write, Install, Build, and Test), each stage gets its own per-stage median across trials. Per-stage medians need not sum to the median total time.
Bar Scaling: Stacked bars scale their segments to fill the total but label the real values. Each segment's width is scaled by the total median divided by the sum of the stage medians, so the plotted segments fill the total median bar. The label on each segment shows its unscaled stage median. Step mean values do sum to the total mean, apart from untimed intervals between steps.
Derived Views: Interactive subset sums on the charts are derived views. For each trial, a derived view sums the selected sandboxes or steps only when every selected component has a valid value. It summarizes those trial sums with the same nearest-rank estimators and the same 20-trial tail floor, without a confidence interval.
Provider Scores
Averages four scores for each provider, each from 0 to 100: workload time, workload cost, sandbox operations and machine performance.
A unit's score is averaged up to its suite, then its subsection, then workload time and cost; an operation's or machine test's score compares providers in its default configuration. A child without results adds 0.
- Inputs
-
Score on the test's default configuration
- Aggregation
-
Arithmetic Mean
- Levels
-
| Label | Section |
| Workload Time Score Time | Workloads |
| Workload Cost Score Cost | Workloads |
| Sandbox Operations Score | Sandbox Operations |
| Machine Performance Score | Machine Performance |
- Rank
-
Every level of the tree is ranked: providers are ordered by the level's published score, higher first, and tied providers share the better rank (1, 2, 2, 4). The count of providers is the number with a score at that level; a provider with none has no rank.
- First Test Only
-
- Added Tests
-
| Tests |
| Read 4 KiB, Write 4 KiB |
Higher is better. A provider without a result on a child loses its points; nothing is substituted for it.
Time Score
Averages each provider's workload time scores, from 0 to 100, where 100 is the fastest provider on every unit.
A unit scores 100 times the best provider's time divided by this provider's time; a workload scores the mean over its whole unit set, and the composite is the mean of the workload scores. A unit without a result adds no points.
- Inputs
-
- Aggregation
-
Arithmetic Mean
Higher is better. A provider that fails a unit or a workload loses points; nothing is substituted for it.
Cost Score
Averages each provider's workload cost scores, from 0 to 100, where 100 is the cheapest provider on every unit.
A unit scores 100 times the best provider's cost divided by this provider's cost; a workload scores the mean over its whole unit set, and the composite is the mean of the workload scores. A unit without a result adds no points.
- Inputs
-
- Aggregation
-
Arithmetic Mean
Higher is better. A provider that fails a unit or a workload loses points; nothing is substituted for it.
Independence
Principle: No trial's result may depend on another trial: not on what ran before it, what ran beside it, what an earlier trial left behind, how busy the job host was, or how much of the account's limits other trials used. Provider-wide features that every customer receives (base-image caches, host placement, and warm pools) stay in the result and are recorded in the trial data.
Execution Rules
Every run enforces these rules to keep trials apart:
- Fresh sandbox isolation: Every trial runs in its own fresh sandbox, unless a multi-sandbox benchmark states an exception (R2E-Gym, Sequential TTI, Snapshot and Restore, and Fork). Several passes, calls, or rows inside one sandbox count as one trial sample, not several independent samples.
- Equal concurrency and rate-limit spacing: Every provider runs the same number of trials in parallel, which each benchmark declares and which fits both the job host's concurrency cap and the provider's documented concurrent-sandbox limit. When a provider documents a sandbox-creation rate limit, the benchmark script spaces
create calls to that limit outside all timed steps and records the spacing in the run manifest. - Shuffled, interleaved rounds: A provider's benchmark order is shuffled once per run, with the random seed recorded in the run manifest. Each benchmark's trials are spread across 4 rounds interleaved with the other benchmarks, so trials come from separate moments across the run rather than one block.
- Explicit cache control: Provider image caches stay enabled, because every customer receives them. Guest page and package caches are cleared or bypassed wherever a benchmark specifies cold execution.
- Dedicated job hosts: Each provider's chain runs on its own dedicated cloud job host, so one provider's workload cannot slow another provider's timings.
- Sequential worker cleanup and in-flight tracking: A worker starts a new trial only after every resource from its previous trial is confirmed deleted. A trial may share provider hosts, disks, and networks with other trials of its batch that run at the same time, and each trial records how many of them were in flight over its window.
- Empty account verification: The benchmark account holds no live sandboxes besides the run's own. The benchmark script checks this before each provider's chain starts and records the count of other live sandboxes at the start and end of every batch.
- Untimed placement and load probes: Outside timed steps, each trial records, on a best-effort basis, the sandbox's visible CPU model and count (
nproc), kernel version, and the device and file system behind the working directory, together with the job host's CPU and memory load over the trial's window. No provider host or zone identifier is collected. - Pre-publication statistical audit: Every completed run is audited before publication by the statistical run checks below, with thresholds fixed before the run.
- Objective outliers: A value is excluded as implausible only by the outlier and physical-limit checks below, with its reason, and an excluded trial stays in the data as excluded.
Statistical Run Checks
Coverage: The audit covers every provider-benchmark pair with at least 15 successful trials, on each main metric, using one data point per trial. Pairs with fewer successful trials are skipped and listed as unassessed in the run report, and so are checks whose required per-trial placement facts, job-host load facts, or round numbers were not recorded.
Correction: The comparison checks use two-sided rank-based tests with Benjamini-Hochberg false-discovery-rate correction at q = 0.05.
The correction covers one family of failure-eligible checks (trend, first round, round start, lag 1, overlap, host saturated, and the time-clustering runs test of tier split) across all pairs in the run. The host-type check produces notes only and is corrected separately.
Failure Rule: A check fails a pair only when the effect is both large and significant: the observed effect must reach its minimum effect size, and its corrected q-value must be below 0.05. Significant effects smaller than the minimum effect size are reported as notes and never fail a pair.
Two findings fail without a significance test: the saturated-share fallback of the host-saturated check, and a better-side finding of the outliers check.
Detectable Effect: Each pair also reports the smallest effect the audit could detect. The audit reports the smallest effect detectable at 80% power at the corrected threshold, so a passing audit on a small sample is not over-interpreted.
Resolution: A failing pair stays out of publication until the failure is resolved. It is resolved when its outlier trials are excluded under the objective outlier rule, the effect is explained in the record, or the pair is re-run.
Checks: The table lists each check, what it tests, and when it fails a pair.
| Check | What It Tests | When It Fails |
| Trend | Fitted drift of the metric across trial execution order, expressed as a percentage of the pair's median. | Fails at an effect of at least 8.0% with q < 0.05. |
| First Round | The trials of the first round compared against those of the later rounds, with at least 5 trials in each group. | Fails at an effect of at least 15.0% with q < 0.05. |
| Round Start | The initial trials of each round (started before any trial of that round finished, right after another benchmark's round) compared against the rest of the same round as ratios to the round median, with at least 5 trials in each group. | Fails at an effect of at least 15.0% with q < 0.05. |
| Lag 1 | Lag-1 rank autocorrelation (ρ) of trial residuals within a batch, after removing the batch median and a Theil-Sen trend. | Fails when |ρ| is at least 0.5, |ρ| times the series' robust spread is at least 8.0% of the median, and q < 0.05. |
| Overlap | Trials running alongside other trials of the same batch (a time-weighted mean of at least 1.0 other trials holding a sandbox) compared against solo trials (at most 0.1 other trials), normalized by the batch median, with at least 5 trials in each group. | Fails at an effect of at least 15.0% with q < 0.05. |
| Host Saturated | Trials recorded while the job host was saturated (peak CPU at or above 0.9 of its limit, any CPU throttling, memory pressure (some avg10) at or above 10.0%, or, where pressure files are absent, less than 262144 KiB of available memory) compared against cool trials. | With at least 5 hot and 5 cool trials, fails at 15.0% slower or more with q < 0.05. With fewer of either, fails when the saturated share of trials is at least 0.25, and otherwise attaches a note. |
| Tier Split | A bimodal hardware or placement split in a group of at least 8 trials (at least 3 of them in the smaller tier) where Ashman's D reaches the floor for the group's size n (6.3 for n ≥ 8, 5.0 for n ≥ 20, 4.2 for n ≥ 40, 3.6 for n ≥ 100, and 3.4 for n ≥ 200). | Attaches a note when the tier ratio is at least 1.3. Fails only when the tier ratio is at least 1.8, the smaller tier has at least 5 trials, and the tiers cluster in time on a runs test (q < 0.05); a split that does not cluster in time reflects host placement and does not fail. |
| Host Type | The median of trials grouped by recorded CPU count and model (at least 3 trials) compared against the most common group (at least 5 trials), corrected separately from the failure family. | Never fails a pair. Attaches a placement note when the difference is at least 20.0%. |
| Outliers | Isolated extreme values in a group of at least 8 trials, defined as at most max(1, 0.05 × n) trials lying beyond 6.0 robust sigmas of the median and at least 2.0 times away from it. | Slow spikes are kept and reported. An isolated value on the better side of the median is excluded as implausible unless its own trial's physical-limit or cache check explains it; where the test's steps include create or a build, a better-side value is kept as the provider's warm pool (and noted) unless a recorded fact ties the trial to an earlier one. |
| Cache Flags | Per-trial physical-limit and cache checks (a throughput ceiling, a cached direct read, a CPU-bound run, and CPU throttling), each evaluated from the trial's own data. | Never fails a pair or excludes a trial. When a check fires in at least 15% of a pair's trials, a ceiling or a cached direct read is stated on the benchmark page, and a CPU-bound run or throttling is recorded as a note. |