> ## Documentation Index
> Fetch the complete documentation index at: https://boxrouter.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Choose a Sandbox Image

> Pick the image a sandbox starts from, check the shell tools it needs, and prepare it ahead so first starts are fast.

An image supplies the operating system, runtimes, tools, and dependencies a sandbox starts with. Leave it out and you get BoxRouter's standard image. Bring your own when the job needs software the standard image lacks.

**Before You Begin:** `BOXROUTER_API_KEY` set and a client installed, as in the [quickstart](https://boxrouter.ai/docs/quickstart.md).

## Image, Volume, or Pause

| You Need | Use | It Captures |
| - | - | - |
| The same tools in every new sandbox | An image | Software, fixed before the sandbox starts |
| Files that change while it runs | A [Volume](https://boxrouter.ai/docs/volumes.md) | One folder, kept after the sandbox is destroyed |
| The same sandbox back, as it was | [Pause](https://boxrouter.ai/docs/pause-resume.md) | The sandbox's files, and its memory where the route reports it, until it resumes or reaches `kept_until` |

An image never holds work in progress. Put stable setup in the image and the rest in a volume.

## The Standard Image

It is Ubuntu 24.04 on `linux/amd64` with `bash`, `python3`, `pip`, `node`, `npm`, `git`, and `curl`. It provides a writable `/workspace` directory. Use `/tmp` for temporary files and a [Volume](https://boxrouter.ai/docs/volumes.md) for files you need to keep.

Catalog agents start from an agents image that adds them, and GitHub Actions jobs run a separate runner image. [Image Contents](https://boxrouter.ai/docs/image-contents.md) lists the versions in each and which one runs when.

## Bring Your Own Image

The `image` field takes any of these forms.

| Form | Value |
| - | - |
| Omitted | The standard image |
| OCI reference | `"python:3.13-slim"` |
| Dockerfile | `{"dockerfile": "FROM python:3.13-slim\n"}`, with `context_artifact_id` for an [uploaded](https://boxrouter.ai/docs/reference/upload-artifact.md) build context |
| Private registry | `{"private_oci": "registry.example.com/team/agent:1.4"}` beside `registry_credentials` |
| Provider artifact | `{"provider_artifact": "..."}`, on exactly one provider |
| `"provider_default"` | The provider's own default image |

```python Python theme={"theme":"css-variables"}
from boxrouter import BoxRouter

with BoxRouter() as client:
    with client.sandboxes.create(image="python:3.13-slim") as box:
        print(box.exec("python3 --version").stdout, end="")
```

```typescript TypeScript theme={"theme":"css-variables"}
import { BoxRouter } from '@boxrouter/sdk';

await using client = new BoxRouter();
await using box = await client.sandboxes.create({ image: 'python:3.13-slim' });
process.stdout.write((await box.exec('python3 --version')).stdout);
```

```bash CLI theme={"theme":"css-variables"}
(
  set -euo pipefail
  id=''
  cleanup() {
    if [[ -n "$id" ]]; then
      boxrouter destroy "$id" >/dev/null || { echo "Check sandbox $id for cleanup." >&2; exit 1; }
    fi
  }
  trap cleanup EXIT
  id=$(boxrouter create --image '"python:3.13-slim"')
  [[ -n "$id" ]] || { echo 'No sandbox ID returned.' >&2; exit 1; }
  boxrouter exec "$id" 'python3 --version'
)
```

Your patch version depends on the image the tag resolves to when you run the command.

Output

```text theme={"theme":"css-variables"}
Python 3.13.18
```

On Credits, a sandbox runs the standard image, the catalog agents image, or a `provider_artifact` that already exists on its provider. Any other
image is refused with `409 policy_denied`. To run it, [connect your own provider key](https://boxrouter.ai/docs/provider-keys.md).

A provider that cannot run your image is left out of routing. Run `boxrouter match` with the same `image` to list the routes that can, before you create.

### What the Image Must Contain

Every image needs `bash` and `sha256sum`. Commands also use `/bin/sh`, `mkdir`, `rm`, `base64`, `tr`, GNU `timeout`, `sleep`, `head` and `wc`, which enforce the deadline and capture output. On Alpine, `apk add --no-cache bash coreutils` covers them. A [terminal](https://boxrouter.ai/docs/connect.md) also needs `python3`.

Image preparation does not check that those tools are installed. A missing tool shows up on the first command, as `422 unsupported_feature` with `details.prerequisite` naming the tool.

### Private Registries

A private image runs only on your own provider keys; a request that could only use a BoxRouter-managed account is refused. BoxRouter hands the registry credentials to the provider once, to set up its copy, and neither stores nor logs them. `registry_credentials` is request-only. The [create reference](https://boxrouter.ai/docs/reference/create.md) has the SDK helpers.

## Prepare Before the First Sandbox

The first time a provider account runs an image, BoxRouter sets it up there. Sandbox creation waits for setup. The sandbox's `image_setup.duration_ms` records the wait, and a launch reports it as its `preparing_image` phase, which `boxrouter launch` prints while it runs. Later sandboxes on that account reuse the copy. BoxRouter can remove an unused copy; the next sandbox sets it up again.

A new tag, or new contents under the same tag, is a new image with its own setup. If you build on every deploy, prepare the image in the pipeline right after you push it.

Pass the same request body to `images.prepare` that you would use to create the sandbox. Set `top`
to the number of providers to prepare on. BoxRouter ranks providers for that request at preparation
time and prepares the first `top` providers. The SDKs and `boxrouter images prepare` wait until no
provider is queued or preparing, then return each provider's `state`.

```python Python theme={"theme":"css-variables"}
from boxrouter import BoxRouter

with BoxRouter() as client:
    status = client.images.prepare(match={"image": "python:3.13-slim"}, top=2)
    for provider in status.providers:
        print(provider.provider, provider.state, provider.reason or "")
```

```typescript TypeScript theme={"theme":"css-variables"}
import { BoxRouter } from '@boxrouter/sdk';

const client = new BoxRouter();
try {
  const status = await client.images.prepare({ match: { image: 'python:3.13-slim' }, top: 2 });
  for (const provider of status.providers) console.log(provider.provider, provider.state, provider.reason ?? '');
} finally {
  await client.close();
}
```

```bash CLI theme={"theme":"css-variables"}
boxrouter images prepare --match '{"image": "python:3.13-slim"}' --top 2 |
  jq -r '.providers[] | "\(.provider) \(.state) \(.reason // "")"'
```

A provider whose `state` is `ready` has a usable copy. Any other final state, such as `failed`, says why in `reason`.

If preparation outlasts the SDK's wait, the call raises a timeout error and setup continues in the background;
`images.preparations` reads the state later. Pass `wait=False` (`{ wait: false }` in TypeScript, `--no-wait` in the CLI)
to return the started status at once.

The HTTP API starts preparation with `POST /images/prepare` and reads progress from `GET /images/preparations`.

Prepared copies remain in the provider account and can incur storage charges; remove them when done.

A sandbox created on a provider account with a `ready` copy has no `image_setup`. To check a live
sandbox, run `boxrouter get SANDBOX_ID` and inspect `image_setup`. Ranking is a snapshot from
when you prepared, so a later sandbox can land on a provider you skipped.

If the chosen provider has no ready copy, creating the sandbox sets one up itself, or waits for a
setup already running. If setup outlasts the create request's time limit, the request fails with:
`Still setting up your image on <Provider>; try again shortly.` Setup continues in the background.

### Remove a Copy

Replace `PROVIDER_ID` with a provider from the preparation result, then request removal:

```python Python theme={"theme":"css-variables"}
from boxrouter import BoxRouter

with BoxRouter() as client:
    result = client.images.unprepare("python:3.13-slim", ["PROVIDER_ID"])
    for provider in result.providers:
        print(provider.provider, provider.state, provider.reason or "")
```

```typescript TypeScript theme={"theme":"css-variables"}
import { BoxRouter } from '@boxrouter/sdk';

const client = new BoxRouter();
try {
  const result = await client.images.unprepare('python:3.13-slim', ['PROVIDER_ID']);
  for (const provider of result.providers) console.log(provider.provider, provider.state, provider.reason ?? '');
} finally {
  await client.close();
}
```

```bash CLI theme={"theme":"css-variables"}
boxrouter api images unprepare --body '{"image": "python:3.13-slim", "providers": ["PROVIDER_ID"]}' |
  jq -r '.providers[] | "\(.provider) \(.state) \(.reason // "")"'
```

For a Dockerfile build, pass the `sha256:` selector that `images.prepare` returns as `image`.

An `absent` entry means that copy was removed or was not present. A retained copy stays `ready`,
with a `reason` explaining why. Check every entry; a provider can have more than one copy.
Removal has two restrictions:

* It deletes only copies BoxRouter created and recorded as its own. It leaves other copies alone
  because it cannot tell whether another deployment on the same provider account uses them.
* It never deletes a copy used by an active sandbox.

You can free that space by deleting the image or template in your provider account.

When image storage is full, creating a sandbox or preparing an image can fail with
`429 rate_limited`. Its `fix` says what to do.

[Prepare an image](https://boxrouter.ai/docs/reference/prepare-image.md) lists every field and limit. It also covers Dockerfile builds and uploaded contexts.

## Next Steps

> [card] [Create a Sandbox](https://boxrouter.ai/docs/sandboxes.md)
>
> What a sandbox request can ask for.

> [card] [Run Commands](https://boxrouter.ai/docs/commands.md)
>
> Exit codes, timeouts, and output limits.

> [card] [Keep Files across Sandboxes](https://boxrouter.ai/docs/volumes.md)
>
> Keep files when a sandbox ends.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.