# Sandboxes

> Sandboxes (beta): Linux microVMs your code creates through the API or the CLI. Each boots from a template in seconds, runs commands and moves files, pauses for the price of its storage, and forks into many live copies.

URL: https://prized.dev/docs/sandboxes

Each sandbox is its own Firecracker microVM, with its own kernel. Sandboxes live in your workspace next to your boxes, draw on the same balance, and answer to the same API keys.

## Sandboxes and boxes [#sandboxes-and-boxes]

|                   | Box                                                             | Sandbox                                                                               |
| ----------------- | --------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| For               | You, working on it every day                                    | Your code and agents, one job at a time                                               |
| Made by           | The dashboard or `prized box new`                               | The [API](https://prized.dev/docs/sandboxes-api), the [CLI](https://prized.dev/docs/cli#sandboxes), or the dashboard      |
| Lives             | Until you delete it                                             | Until you delete it, or 24 hours at most                                              |
| Starts            | Under a minute                                                  | Seconds                                                                               |
| You reach it with | `prized ssh`, sync, ports, the browser terminal                 | Commands and file copies through the API, or `prized sandbox exec` and `cp`           |
| User              | Your login user, with sudo                                      | `prized`, without sudo                                                                |
| Billed            | By size, hourly from a monthly price ([Billing](https://prized.dev/docs/billing)) | Per second, by vCPU, memory, and disk ([Billing: Sandboxes](https://prized.dev/docs/billing#sandboxes)) |

* **Use a box** for the machine you work on: your repo, editor, synced folders, dev servers, and agents that run for days.
* **Use a sandbox** to run code you did not write or have not read (an agent's tool call, a user's snippet), one clean machine per request or test run, the same job fanned out across many copies, or anything that should be gone afterwards.

## Quickstart [#quickstart]

You need an API key with access to sandboxes. Any workspace can use them: on the free credit within the [free tier](#free-tier), and with the full quotas once it has paid in ([Who can use sandboxes](#who-can-use-sandboxes)). Make the key under **Dashboard → Settings → API keys** with **Full access**, or **Custom** with **Sandboxes: Write**, and export it:

```bash
export PRIZED_API_KEY=dcp_…
```

Each tab below creates a sandbox from `prized-base-v1`, runs one command, and deletes it. The CLI needs `prized` v0.21.0 or later, and uses your `prized login` or `PRIZED_API_KEY` ([CLI](https://prized.dev/docs/cli#sandboxes)).

**curl**

```bash
API=https://api.prized.dev/api/v1
AUTH="Authorization: Bearer $PRIZED_API_KEY"
CREATED=$(curl -s -X POST "$API/sandboxes" -H "$AUTH" -H "Content-Type: application/json" -d '{"template":"prized-base-v1"}')
SBX=$(echo "$CREATED" | jq -r '.sandbox.id // empty'); echo "${SBX:-$CREATED}"
# sbx_3f9c0a1b2d4e5f60, or the refusal, such as {"error":"insufficient_balance"}
for i in $(seq 60); do STATE=$(curl -s "$API/sandboxes/$SBX" -H "$AUTH" | jq -r .sandbox.state); [ "$STATE" = starting ] || break; sleep 1; done; echo "$STATE"
# running
TOOLBOX=$(curl -s "$API/sandboxes/$SBX" -H "$AUTH" | jq -r .sandbox.toolboxBase)
curl -s -X POST "$TOOLBOX/process/execute" -H "$AUTH" -H "Content-Type: application/json" -d '{"command":"python3 --version"}'
# {"stdout":"Python 3.12.3\n","stderr":"","exitCode":0,"durationMs":41}
curl -s -X DELETE "$API/sandboxes/$SBX" -H "$AUTH" > /dev/null
```

**CLI**

```bash
ID=$(prized sandbox create)
prized sandbox exec $ID -- python3 --version
# Python 3.12.3
prized sandbox rm $ID
```

A create on warm capacity is usually running in about five seconds; at busy moments it can wait in `starting` while capacity is added. Every field of a create is on [Sandbox API: Create](https://prized.dev/docs/sandboxes-api#create).

## States [#states]

| State      | Meaning                                                                                                                                                                      |
| ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `starting` | Booting from its template, or coming back from a pause.                                                                                                                      |
| `running`  | Up: commands and files work, and it bills at the running rate.                                                                                                               |
| `pausing`  | Saving its memory and disk.                                                                                                                                                  |
| `paused`   | Memory and disk saved, no compute billed. Commands and files answer `409 sandbox_not_running`.                                                                               |
| `resizing` | Changing shape while paused; it returns to `paused`, or goes to `error` if the resize fails partway.                                                                         |
| `deleting` | Being torn down.                                                                                                                                                             |
| `deleted`  | Gone: reads answer `404 not_found`.                                                                                                                                          |
| `error`    | Never came up, or lost the machine it ran on; `errorReason` says why. It costs nothing and is removed after `autoDeleteMin`, or after an hour. Delete it and create another. |

## Pause and resume [#pause-and-resume]

A paused sandbox keeps its memory as well as its disk: on resume, every process carries on where it stopped. A single resume is usually back to `running` within two seconds. Pause and resume answer `202` at once; read the sandbox until it reaches `paused` or `running`.

* **Paused costs storage only.** Disk and saved memory bill at the storage rate, no compute ([Billing: Sandboxes](https://prized.dev/docs/billing#sandboxes)).
* **Paused still holds its quota.** A paused sandbox keeps its CPU and memory reserved for its resume, so it counts against the workspace's running CPU and memory until you delete it ([Limits: Sandboxes](https://prized.dev/docs/limits#sandboxes)).
* **The API never wakes a sandbox.** Exec and file requests on a paused sandbox answer `409 sandbox_not_running`; resume first. The CLI's `exec` and `cp` resume a paused sandbox for you.

## Automatic pause and delete [#automatic-pause-and-delete]

Three create fields bound a sandbox's life, so a forgotten one stops costing money:

| Field           | Default                                             | Effect                                                                                              |
| --------------- | --------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| `autoPauseMin`  | Off                                                 | Pause after this many minutes with no activity.                                                     |
| `autoDeleteMin` | Off                                                 | Delete after this many minutes paused.                                                              |
| `maxRuntimeMin` | 1,440 (24 hours); 60 on the [free tier](#free-tier) | Delete this many minutes after the create, whatever its state. Up to 1,440, or 60 on the free tier. |

Exec and file requests count as activity, and a command keeps its sandbox awake for as long as it runs. `POST /api/v1/sandboxes/{id}/refresh` counts too, for an idle stretch with nothing to run. A resumed sandbox starts its idle count again.

> [!WARNING]
> Deleting a sandbox, by hand or by `autoDeleteMin` or `maxRuntimeMin`, erases its disk and memory, with no undo. Copy out what you need first.

## Fork [#fork]

```bash
curl -s -X POST "$API/sandboxes/$SBX/fork" -H "$AUTH" -H "Content-Type: application/json" -d '{"count":8}'
```

A fork turns one running sandbox into `count` live copies: each child has the parent's memory, disk, and every running process, as they were at the fork, and the parent keeps running. Children are ordinary sandboxes with their own ids, billed and counted like any other.

* **The parent must be `running`.** Fork it before you pause it.
* **Up to 256 copies a request.** All of them start on the machine that runs the parent; if it has room for fewer, the fork answers `409 fork_capacity` with `fits`, and nothing is made.
* **Quota counts every child.** A fork that would pass a quota answers `429 sandbox_quota_exceeded` with `admitted`, how many would have fit, and makes none.
* **Children inherit** the template, shape, [environment variables](#environment-variables), labels (unless the fork names its own), `autoPauseMin`, `autoDeleteMin`, and the parent's deletion time: a child never outlives its parent's `maxRuntimeMin`.

## Resize [#resize]

A paused sandbox can change its vCPU, memory, and disk within its template's bounds; disk only grows. The next resume boots fresh from the resized disk, so the paused memory and processes are discarded. A resize needs room in your quota for the larger of the old and new shape until it completes ([Sandbox API: Resize](https://prized.dev/docs/sandboxes-api#resize)).

## Templates [#templates]

| Template          | What is in it                                                                                                                     |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `prized-base-v1`  | Ubuntu 24.04, build-essential, git, curl, jq, ripgrep, unzip, Python 3 with pip and venv, Node.js 22 with npm, corepack, and pnpm |
| `prized-agent-v1` | Everything in `prized-base-v1`, plus Claude Code and Codex                                                                        |

* **Shape.** Both default to 4 vCPU, 8 GB of memory, and a 10 GB disk, and take 1 to 8 vCPU, 1 to 16 GB of memory, and 10 to 100 GB of disk (2 vCPU and 4 GB at most on the [free tier](#free-tier)). `GET /api/v1/sandbox-templates` lists each template with its defaults and bounds.
* **A template never changes.** Its tools are pinned; newer versions arrive as a new template id (`prized-agent-v2`), so a sandbox made today matches one made next month.
* **Agents bring their own sign-in.** Nothing is signed in on `prized-agent-v1`: pass the key the CLI reads, such as `ANTHROPIC_API_KEY` for Claude Code, in `envVars`.

## Inside a sandbox [#inside-a-sandbox]

|                            |                                                                                                                               |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| User                       | `prized`, without sudo                                                                                                        |
| Home                       | `/home/prized`                                                                                                                |
| Working directory          | `/home/prized/work`, where commands run unless they name another `cwd`                                                        |
| Shell                      | Commands run under `bash -c`                                                                                                  |
| Files reachable by the API | Anything under `/home/prized`; symlinks are refused                                                                           |
| Network                    | Outbound internet. Nothing reaches a sandbox from the internet, and sandboxes cannot reach each other                         |
| Installing more            | No root, so no `apt`. `pip` installs into a venv, and `npm install -g` needs a user prefix (`npm config set prefix ~/.local`) |

## Environment variables [#environment-variables]

`envVars` on the create are set for every command the sandbox runs, and for every command in its forks; a command's own `env` overrides them name by name. Up to 32, each name a shell identifier (letters, digits, and `_`, not starting with a digit) of up to 64 characters, each value up to 16 KiB.

They are stored encrypted, never returned by any route, and erased with the sandbox, so `envVars` is the place for a key the sandbox's code needs.

## Who can use sandboxes [#who-can-use-sandboxes]

* **Owners and members** of a workspace, and API keys they made. [Contractors](https://prized.dev/docs/teams#roles) do not see sandboxes; their requests answer `404 not_found`.
* **Any balance above zero.** A workspace on the [free credit](https://prized.dev/docs/billing#free-credit) alone uses sandboxes within the [free tier](#free-tier); one that has paid in (a plan or a [top-up](https://prized.dev/docs/billing#topping-up)) gets the full [quotas](https://prized.dev/docs/limits#sandboxes). An empty balance cannot start one (`409 insufficient_balance`).
* **API keys.** A key with **Full access** reaches sandboxes. A **Custom** key needs **Sandboxes: Read** to list them and **Sandboxes: Write** for everything else, running commands and moving files included. A key limited to some boxes, and the key an agent got at [agent sign-in](https://prized.dev/docs/agent-sign-in), reach no sandboxes ([What a key can do](https://prized.dev/docs/workspaces#what-a-key-can-do)).

## Free tier [#free-tier]

A workspace whose balance is only free credit (it has not paid in with a plan or a top-up) runs sandboxes within these limits:

| Free tier                                     | Limit                                                                                                      |
| --------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| Sandboxes at once                             | 4, counted like the [sandbox quota](https://prized.dev/docs/limits#sandboxes): paused ones and ones in `error` count         |
| Running vCPU and memory, across the workspace | 8 vCPU and 16 GB                                                                                           |
| Per sandbox                                   | Up to 2 vCPU and 4 GB of memory. With no shape asked for, the template's default capped at 2 vCPU and 4 GB |
| Lifetime                                      | 60 minutes: `maxRuntimeMin` defaults to 60, and a larger value is refused                                  |
| Disk                                          | 40 GB across the workspace's sandboxes                                                                     |

* **It spends the free credit.** Sandboxes draw on it at the [sandbox rates](https://prized.dev/docs/billing#sandboxes), like any compute; an empty balance answers `409 insufficient_balance`.
* **Past a limit** the API answers as for any quota (`429 sandbox_quota_exceeded` with its `reason`, or `422 invalid_resources`), with `"tier": "free"` in the body and the limit it hit as `limit`: sandboxes for `sandbox_limit`, vCPU for `running_vcpu` and `vcpu`, MB for `running_memory` and `memoryMb`, GB for `retained_disk`, minutes for `max_runtime`. For example, `{"error": "sandbox_quota_exceeded", "reason": "running_memory", "tier": "free", "limit": 16384}`. A [top-up](https://prized.dev/docs/billing#topping-up) or a plan lifts the workspace to the full quotas.
* **Sandboxes made while paying** resume on the free tier only if they fit it: one bigger than 2 vCPU or 4 GB answers `422 invalid_resources` (resize it down first), and one with more than an hour left before it is deleted answers `429 sandbox_quota_exceeded` with `reason` `max_runtime`. Its lifetime is never shortened.
* **A resize** stays within 2 vCPU and 4 GB, and is accepted when the resized sandbox fits the free tier's totals, so shrinking one brings a workspace back inside them.
* **The free credit's rules hold.** One process at full CPU for 30 minutes pauses the workspace's boxes and sandboxes ([Free credit](https://prized.dev/docs/billing#free-credit)).
* **Switched off.** If free-tier access is switched off for your workspace, a create, resume, or fork on the free credit alone answers `409 paid_balance_required` until the workspace has paid in.

## Dashboard [#dashboard]

**Sandboxes** in the dashboard's sidebar lists every sandbox in the workspace with its template, shape, state, and what it costs an hour, against the workspace's quotas. Create, pause, resume, and delete from there. A sandbox's own page shows its countdowns to auto-pause and deletion, live CPU and memory, its processes, and its events. The **Templates** tab lists the catalog, and **API** shows these requests with your workspace's addresses.

## Not yet [#not-yet]

| Not yet                                                                        | Instead                                                                                     |
| ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------- |
| Templates from your own Dockerfile or image, or saving a sandbox as a template | Install what you need at the start of each run, or once in a sandbox you then [fork](#fork) |
| Forking a box into sandboxes                                                   | Fork a sandbox                                                                              |
| Egress allow and deny lists                                                    | Every sandbox has outbound internet                                                         |
| Sandboxes that outlive 24 hours                                                | A [box](https://prized.dev/docs/boxes)                                                                        |
| More than 8 vCPU or 16 GB of memory                                            | A bigger [box](https://prized.dev/docs/machines#sizes)                                                        |
| GPUs                                                                           |                                                                                             |
| Regions other than `us-west-2` (Oregon)                                        |                                                                                             |
| SSH or a terminal into a sandbox, or a preview link to one of its ports        | Commands and file copies through the API, or `prized sandbox exec` and `cp`                 |
| Streaming output                                                               | Write output to a file and read it with a later exec or a download                          |
| TypeScript and Python SDKs                                                     | The [API](https://prized.dev/docs/sandboxes-api) with any HTTP client, or the CLI                             |
| An MCP server                                                                  | The [API](https://prized.dev/docs/sandboxes-api) or the CLI                                                   |
