Sizing and limits
Every sandbox runs on a named resource contract.
Pick a size by name; the raw CPU, memory, and disk fields are compatibility inputs that must exactly match a published contract.
Sizing reference
| Size name | vCPU | RAM | Disk | Notes |
|---|---|---|---|---|
xs | 1 | 2 GB | 10 GB | Lightweight scripts |
small | 2 | 4 GB | 10 GB | Default for agent and build workloads |
medium | 4 | 8 GB | 20 GB | Larger builds and parallel tests |
large | 8 | 16 GB | 40 GB | Heavy test suites |
xl | 16 | 32 GB | 80 GB | Intensive parallelism |
Use the default small contract for Claude Code, Codex, package installation, ordinary application builds, and agent execution.
Choose medium only when measured CPU, memory, or disk requirements exceed small.
Raw resource fields are compatibility inputs and must exactly identify a published named contract.
Raw resource overrides (still accepted):
| Field | Default | Validation range |
|---|---|---|
cpu_count | 2 | 1-16 |
memory_mb | 4096 | 2048-32768 |
disk_size_mb | 10240 | Must match the selected named contract |
timeout_sec | 3600 | 1-86400 |
idle_timeout_sec | 0 (disabled) | 0-86400 |
always_on | false | Not required for most agent work |
persistent | true | Preserve sandbox state across pauses and resumes |
Set always_on=True to disable timeout_sec enforcement entirely. Useful for long-lived development environments or hosted IDEs. Set idle_timeout_sec to stop an abandoned persistent session after a period of inactivity; it is off unless you set it. Set persistent=False only for one-off jobs where the filesystem should be discarded on stop or timeout.
Billing notes
Billing accrues from state = "running" until state = "paused" or state = "destroyed".
- Running: billed at the vCPU-second + GiB-second rate for your plan
- Paused: billed at storage rate only (disk GiB-second)
- Destroyed: billing stops immediately
Template readiness and available sizes are reported by the catalog and can vary by environment.
Runner endpoints (opt-in preview)
The SOMA one-hop runner is an opt-in fast path for create, exec, and destroy: your SDK talks straight to the runner instead of going through the control plane first. Everything documented here keeps working unchanged whether or not you opt in.
https://run-us.miosa.aiworks for every call. Opting in sends requests there instead ofhttps://api.miosa.ai; EU is on the roadmap, but keys do not carry a region yet. This endpoint is unrelated to a sandbox’s placement region (us-west,us-east,us-mia).- A request for a sandbox that lives on a different host inside the runner fleet is forwarded internally; you almost always get an answer from
run-us.miosa.aidirectly (421is a rare fallback if that internal forward fails, naming the correct URL asrunner_url). For lower latency, everycreateresponse also names that sandbox’s own runner URL as aSoma-Runner-Urlresponse header (https://<tag>.run-us.miosa.ai); official SDKs read it and call that URL directly from then on. This is an automatic optimization, not something you need to do yourself. createon the runner only serves the default template,xssize, non-persistent sandboxes. A request outside that (a custom template, a snapshot, a customcwd,persistent: true, or a larger size) answers400 RUNNER_UNSUPPORTED_REQUEST; official SDKs send that create tohttps://api.miosa.aiautomatically instead.- The runner only creates non-persistent sandboxes, so idle timeout stays off unless you set
idle_timeout_sec(0-86400 s).timeout_sec(default 3600, max 86400) caps lifetime, andalways_on=Truedisables the timeout entirely for long-lived sandboxes and hosted IDEs. execdefaults to a 30 second timeout, same as the control plane.POST /api/v1/sandboxes/{id}/exec/streamstreams stdout/stderr as server-sent events, ending with an exit event.destroyresponses includecpu_msandlifetime_msfor this sandbox’s usage.429 runtime_busy(withRetry-After: 0) means this runner is at its admission limit; retry the request.429 rate_limitedis a per-key or per-tenant rate limit, same semantics as the control plane.503 feed_stalemeans the runner’s key and policy data is too old to accept new creates; its existing sandboxes keep working.- Runners serve HTTP/3 (QUIC) first and fall back to HTTP/2 automatically if QUIC is blocked on your network. Official SDKs handle the fallback for you.
A raw HTTP client can use either the regional URL or the per-sandbox URL named in the Soma-Runner-Url header:
curl -i -X POST https://run-us.miosa.ai/api/v1/sandboxes
-H "Authorization: Bearer $MIOSA_API_KEY"
-H "Content-Type: application/json"
-d '{"template_id": "miosa-sandbox"}'
# Soma-Runner-Url: https://3.run-us.miosa.ai
curl https://3.run-us.miosa.ai/api/v1/sandboxes/3a1b2c3d-1111-2222-3333-444444444444
-H "Authorization: Bearer $MIOSA_API_KEY" Every runner response adds a Server-Timing header breaking auth, pool, and exec time down in milliseconds.
Errors specific to the runner path
| Status | Code | Cause |
|---|---|---|
| 401 | unauthorized | The key is unknown or revoked in this runner’s in-memory table. |
| 403 | forbidden | The tenant is not enabled for this path, is suspended, or the project is not allowed. |
| 400 | RUNNER_UNSUPPORTED_REQUEST | This create shape is not served by the runner (see above). Official SDKs retry it against https://api.miosa.ai. |
| 429 | rate_limited | Per-key or per-tenant rate limit, same semantics as the control plane. |
| 429 | runtime_busy | This runner is at its admission limit (Retry-After: 0). Retry the request; run-us.miosa.ai resolves to every runner in the region, so a retry can land on a different one. |
| 503 | feed_stale | This runner’s key and policy data is older than its freshness limit (Retry-After: 0). Creates are refused; its existing sandboxes keep working. |
| 421 | misdirected | Rare: the internal forward to the sandbox’s own host failed. The body names the correct URL again as runner_url. Follow it once; do not retry the original URL. |
The full endpoint contract (request and response bodies) is unchanged by runner mode; only the host you send them to changes. See the Sandboxes API for the control-plane reference.