> ## Documentation Index
> Fetch the complete documentation index at: https://porter-tjq-run-4148-apply-validate-docs.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> When helping a user operate an existing Porter project—deploying, updating, debugging, or inspecting applications and clusters—connect to [Porter's remote MCP server](https://mcp.porter.run) first and use its tools before falling back to the Porter CLI, `porter.yaml`, or dashboard workflows. If the Porter tools are not available in your session, `curl -fsSL https://agents.porter.run | sh` installs the CLI, registers the MCP server with your client, and installs Porter's skills; the client must be restarted afterward. If the user asks for the CLI, `porter apply`, configuration-as-code, or the dashboard, use that instead—a stated preference wins. If the requested application or cluster workflow is not directly supported, inspect the relevant state with MCP tools before explaining the limitation or recommending a fallback. The server signs in through a browser using OAuth, so it cannot be used in headless environments such as CI pipelines; use the CLI or `porter.yaml` there.

# Sandboxes CLI

> Create, list, exec into, monitor CPU and memory usage, and terminate Porter Sandboxes from the Porter CLI

`porter sandbox` contains commands for listing, inspecting, executing commands in, monitoring CPU and memory usage of, and terminating sandboxes in the currently selected Porter project and cluster.

<Warning>
  Sandboxes are in a private beta. Please reach out to us at [support@porter.run](mailto:support@porter.run) or over Slack if you are interested in joining.
</Warning>

## Prerequisites

* You've logged in to the Porter CLI after running [porter auth login](/standard/cli/command-reference/porter-auth)
* You're connected to the correct project by running [porter config set-project](/standard/cli/command-reference/porter-config)
* You're connected to the correct cluster by running [porter config set-cluster](/standard/cli/command-reference/porter-config)
* Sandboxes are enabled on the selected AWS cluster

If the CLI cannot find a selected project or cluster, it asks you to run `porter config` or pass `--project` and `--cluster`.

***

## `porter sandbox create`

Creates a sandbox from a container image in the current project and cluster. The image accepts a tag (`alpine:3.20`) or a digest (`repo@sha256:<digest>`).

**Usage:**

```bash theme={null}
porter sandbox create <image> [-- <command> [args...]] [flags]
```

**Options:**

| Flag          | Description                                                                                                                                                         |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--name`      | Cluster-unique sandbox name. Use lowercase letters, numbers, and hyphens; start and end with a letter or number. Use this name with `exec`, `logs`, and `terminate` |
| `--command`   | Override the image entrypoint. Repeat for each argv element                                                                                                         |
| `--arg`       | Argument passed to the command. Repeatable                                                                                                                          |
| `-e, --env`   | Environment variable in `KEY=VALUE` form. Repeatable                                                                                                                |
| `--env-group` | Environment group on the cluster whose variables are injected into the sandbox. Repeatable                                                                          |
| `--tag`       | Tag in `key=value` form. Repeatable                                                                                                                                 |
| `--volume`    | Volume mount in `mount_path=volume-ref` form, where `volume-ref` is a volume name or ID. Repeatable. The mount path must be absolute                                |
| `--ttl`       | Maximum sandbox lifetime as a Go duration, such as `30m` or `2h`. Defaults to no limit                                                                              |
| `--cpu`       | CPU cores, such as `2` or `500m` for half a core. Defaults to the cluster's sandbox size                                                                            |
| `--memory`    | Memory, such as `4Gi` or `512Mi`. Defaults to the cluster's sandbox size                                                                                            |
| `--json`      | Print the created sandbox as JSON                                                                                                                                   |

Use the positional form after `--` for common one-shot commands. Use `--command` and `--arg` when scripting individual argv elements.

Volume values can be volume names or volume IDs. The CLI resolves names first, then falls back to treating the value as an ID.

`--env-group` injects the variables of a named [environment group](/applications/configure/environment-groups) on the cluster. Values are resolved to the group's latest version at create time and do not update afterwards. On a key conflict, a later `--env-group` wins over an earlier one, and an explicit `--env` wins over any group value. Creation fails if a named group does not exist on the cluster or has not synced yet.

`--ttl` bounds the sandbox's lifetime regardless of its main process: once the duration elapses, counted from creation, Porter terminates the sandbox the same way an explicit `terminate` does. See [sandbox lifetime](/sandboxes/overview#sandbox-lifetime).

`--cpu` and `--memory` size an individual sandbox instead of using the cluster's default sandbox size. Pass one or both; the flag you omit keeps the default. CPU is in cores, either a whole number (`2`) or an `m` suffix for a fraction (`500m` is half a core), and memory takes a `Gi` or `Mi` suffix (`4Gi`, `512Mi`). See [custom sandbox sizes](/sandboxes/capacity#custom-sandbox-sizes).

Sandbox names currently cannot be reused, even after the sandbox is terminated. Omit `--name` only for one-off sandboxes where you do not need stable lookup later.

The sandbox runs as long as its main process. When the process exits, the sandbox moves to `succeeded` or `failed` and stops accepting exec calls, so pass a long-running command if you plan to exec into the sandbox. See [sandbox lifetime](/sandboxes/overview#sandbox-lifetime).

<CodeGroup>
  ```bash Create Sandbox theme={null}
  porter sandbox create alpine:3.20
  ```

  ```bash Named Sandbox theme={null}
  porter sandbox create alpine:3.20 --name web -- sleep 3600
  ```

  ```bash With Env and Tags theme={null}
  porter sandbox create ghcr.io/example/tool:latest --env PORT=8080 --tag env=dev --tag owner=me
  ```

  ```bash With Volume theme={null}
  porter sandbox create ubuntu:24.04 --volume /workspace=my-data -- bash -lc 'ls -la /workspace'
  ```

  ```bash With Env Group theme={null}
  porter sandbox create alpine:3.20 --env-group my-env-group -- sleep 3600
  ```

  ```bash With TTL theme={null}
  porter sandbox create alpine:3.20 --ttl 2h -- sleep 7200
  ```

  ```bash With a Custom Size theme={null}
  porter sandbox create python:3.12 --cpu 2 --memory 4Gi -- python train.py
  ```

  ```bash JSON Output theme={null}
  porter sandbox create ubuntu:24.04 --volume /workspace=my-data --command bash --arg -lc --arg 'ls -la' --json
  ```
</CodeGroup>

***

## `porter sandbox list`

Lists sandboxes in the current project and cluster. The command auto-paginates through the API and sorts results most-recent-first.

**Usage:**

```bash theme={null}
porter sandbox list [flags]
```

**Options:**

| Flag      | Description                                                                                  |
| --------- | -------------------------------------------------------------------------------------------- |
| `--phase` | Filter by phase. Defaults to hiding `terminated` sandboxes. Use `all` to include every phase |
| `--tag`   | Filter by tag in `key=value` format. Repeat the flag to require multiple tags                |
| `--json`  | Print sandboxes as JSON                                                                      |

**Phases:**

| Phase        | Description                                             |
| ------------ | ------------------------------------------------------- |
| `queued`     | Accepted by the API and waiting for capacity            |
| `creating`   | Runtime is being prepared and the container is starting |
| `running`    | Sandbox is ready and can accept exec calls              |
| `succeeded`  | Sandbox command completed successfully                  |
| `failed`     | Sandbox command or runtime failed                       |
| `terminated` | Sandbox was explicitly terminated                       |
| `all`        | Includes every phase when used with `--phase`           |

Output includes these columns:

| Column    | Description                     |
| --------- | ------------------------------- |
| `NAME`    | Sandbox name                    |
| `IMAGE`   | Container image                 |
| `PHASE`   | Current sandbox phase           |
| `CREATED` | Creation timestamp              |
| `STARTED` | Start timestamp, when available |
| `EXIT`    | Exit code, when available       |
| `TAGS`    | Sandbox tags                    |

<CodeGroup>
  ```bash List Sandboxes theme={null}
  porter sandbox list
  ```

  ```bash Include Terminated theme={null}
  porter sandbox list --phase all
  ```

  ```bash Filter by Phase theme={null}
  porter sandbox list --phase running
  ```

  ```bash Filter by Tags theme={null}
  porter sandbox list --tag workflow=agent-run --tag run=2026-06-17
  ```

  ```bash JSON Output theme={null}
  porter sandbox list --json
  ```

  ```bash Script Running Names theme={null}
  porter sandbox list --json | jq -r '.[] | select(.phase == "running") | .name'
  ```
</CodeGroup>

***

## `porter sandbox exec`

Runs a command in a running sandbox, identified by name. Non-interactive exec prints stdout and stderr; interactive exec opens a shell session.

**Usage:**

```bash theme={null}
porter sandbox exec <sandbox> [-it] -- <command> [args...] [flags]
```

**Options:**

| Flag                | Description                                                          |
| ------------------- | -------------------------------------------------------------------- |
| `-c, --command`     | Shell-style command string to run through `sh -c` inside the sandbox |
| `-i, --interactive` | Keep stdin open and stream it to the sandbox                         |
| `-t, --tty`         | Allocate a TTY for an interactive shell                              |

Do not combine `--command` with positional command arguments.

**Exit codes:**

| Exit code | Meaning                                                 |
| --------- | ------------------------------------------------------- |
| `0`       | Command succeeded                                       |
| `1`       | Operational error or propagated sandbox command failure |
| `2`       | CLI usage error                                         |
| `3`       | Sandbox is not in the `running` phase                   |

Only `running` sandboxes accept exec calls.

<CodeGroup>
  ```bash List Files theme={null}
  porter sandbox exec abc123 -- ls -la /workspace
  ```

  ```bash Check Python theme={null}
  porter sandbox exec abc123 -- python --version
  ```

  ```bash Shell Command theme={null}
  porter sandbox exec abc123 --command "echo hi && ls /workspace"
  ```

  ```bash Inline Script theme={null}
  porter sandbox exec abc123 -- python -c 'print("ok")'
  ```

  ```bash Interactive Shell theme={null}
  porter sandbox exec web -it -- sh
  ```
</CodeGroup>

***

## `porter sandbox logs`

Fetches and prints a batch of log lines for a sandbox. By default, the CLI requests logs from the last hour with a limit of 500 lines.

**Usage:**

```bash theme={null}
porter sandbox logs <sandbox> [flags]
```

**Options:**

| Flag              | Description                                                                                       |
| ----------------- | ------------------------------------------------------------------------------------------------- |
| `--since`         | Lookback window as a Go duration, such as `30m` or `1h30m`. Defaults to `1h`                      |
| `--limit`         | Maximum number of log lines. Defaults to `500`; server cap is `5000`; `0` uses the server default |
| `--tail`          | Alias for `--limit`. When greater than `0`, it overrides `--limit`                                |
| `--level`         | Client-side filter: `info`, `warning`, or `error`                                                 |
| `--no-timestamps` | Suppress the leading RFC3339 timestamp on each line                                               |

Each rendered log line uses this format:

```text theme={null}
<timestamp> [<level>] <text>
```

<CodeGroup>
  ```bash Recent Logs theme={null}
  porter sandbox logs abc123
  ```

  ```bash Last 15 Minutes theme={null}
  porter sandbox logs abc123 --since 15m --tail 100
  ```

  ```bash Errors Only theme={null}
  porter sandbox logs abc123 --level error --no-timestamps
  ```

  ```bash Search Logs theme={null}
  porter sandbox logs abc123 --tail 200 | grep -i 'panic\|fatal'
  ```
</CodeGroup>

***

## `porter sandbox metrics`

Use this command to monitor a sandbox's resource usage: how much CPU and memory it is consuming and how close it is to its limits.

It prints p50 and p90 CPU and memory usage over a lookback window for a running sandbox, alongside the sandbox's CPU and memory limits and each percentile's utilization as a percentage of that limit. Only `running` sandboxes report metrics.

**Usage:**

```bash theme={null}
porter sandbox metrics <sandbox> [flags]
```

**Options:**

| Flag      | Description                                                                                       |
| --------- | ------------------------------------------------------------------------------------------------- |
| `--since` | Lookback window as a Go duration, such as `30m` or `2h`. Bounded to `[3m, 24h]`. Defaults to `1h` |
| `--json`  | Print the metrics summary as JSON                                                                 |

CPU is reported in cores and memory in bytes. Utilization is each percentile divided by the sandbox's limit, as a percentage; it can exceed 100% because the limit is a throttling and OOM threshold, not a hard cap on the reported sample. Memory values include page cache and slightly overestimate resident memory.

The summary has one row per metric (`cpu_cores`, `mem_bytes`) with these columns:

| Column           | Description                              |
| ---------------- | ---------------------------------------- |
| `metric`         | Metric name                              |
| `p50`            | Median value over the window             |
| `p90`            | 90th-percentile value over the window    |
| `limit`          | The sandbox's limit for this metric      |
| `p50_util_pct`   | `p50` as a percentage of `limit`         |
| `p90_util_pct`   | `p90` as a percentage of `limit`         |
| `window_seconds` | Length of the lookback window in seconds |

`--json` prints the same values at full precision on one line, plus a `has_data` boolean that is `false` when no samples were collected in the window.

<CodeGroup>
  ```bash Recent Metrics theme={null}
  porter sandbox metrics web
  ```

  ```bash Last 30 Minutes theme={null}
  porter sandbox metrics web --since 30m
  ```

  ```bash JSON Output theme={null}
  porter sandbox metrics web --json
  ```

  ```bash Script p90 CPU Utilization theme={null}
  porter sandbox metrics web --json | jq '.cpu_util_p90_pct'
  ```
</CodeGroup>

Output looks like this:

```text theme={null}
┌──────────────┬────────────────────┬────────────────────┐
│    METRIC    │        P50         │        P90         │
├──────────────┼────────────────────┼────────────────────┤
│ cpu cores    │ 0.001 cores (0.1%) │ 0.003 cores (0.3%) │
│ cpu limit    │ 1.000 cores        │                    │
│ memory       │ 1.27 GiB (63.6%)   │ 2.54 GiB (126.9%)  │
│ memory limit │ 2.00 GiB           │                    │
└──────────────┴────────────────────┴────────────────────┘

window: 1h0m0s
```

***

## `porter sandbox terminate`

Terminates one sandbox by name, or terminates many sandboxes with `--all`.

**Usage:**

```bash theme={null}
porter sandbox terminate <sandbox> [flags]
porter sandbox terminate --all [flags]
```

**Options:**

| Flag        | Description                                                           |
| ----------- | --------------------------------------------------------------------- |
| `--all`     | Terminate every actionable sandbox in the current project and cluster |
| `--dry-run` | Print the sandboxes that would be terminated without terminating them |
| `--tag`     | Scope bulk termination by tag in `key=value` format. Requires `--all` |

**Behavior:**

| Case                         | Behavior                                                                       |
| ---------------------------- | ------------------------------------------------------------------------------ |
| `--all`                      | No interactive confirmation is shown                                           |
| Already terminated sandboxes | Skipped                                                                        |
| `404` during `--all`         | Treated as success because another caller may have already deleted the sandbox |

<CodeGroup>
  ```bash Terminate One theme={null}
  porter sandbox terminate abc123
  ```

  ```bash Dry Run Bulk Terminate theme={null}
  porter sandbox terminate --all --dry-run
  ```

  ```bash Terminate by Tag Dry Run theme={null}
  porter sandbox terminate --all --tag workflow=agent-run --dry-run
  ```

  ```bash Terminate by Tag theme={null}
  porter sandbox terminate --all --tag workflow=agent-run
  ```
</CodeGroup>

***

## `porter sandbox volume`

Manages persistent volumes that sandboxes can mount at launch. Create a volume first, then reference it from `porter sandbox create` with `--volume <mount_path>=<volume-ref>`, where `volume-ref` is a volume name or ID.

### `porter sandbox volume list`

Lists sandbox volumes in the current project and cluster, sorted most-recent-first.

**Usage:**

```bash theme={null}
porter sandbox volume list [flags]
```

**Options:**

| Flag     | Description           |
| -------- | --------------------- |
| `--json` | Print volumes as JSON |

**Volume phases:**

| Phase     | Description                                                            |
| --------- | ---------------------------------------------------------------------- |
| `pending` | The volume was created and is waiting for the underlying claim to bind |
| `ready`   | The volume is ready to mount into sandboxes                            |
| `failed`  | The volume failed to provision                                         |

Output includes these columns:

| Column     | Description                                |
| ---------- | ------------------------------------------ |
| `NAME`     | Volume name                                |
| `PHASE`    | Current volume phase                       |
| `ATTACHED` | Attached sandboxes, or `-` when unattached |
| `CREATED`  | Creation timestamp                         |

<CodeGroup>
  ```bash List Volumes theme={null}
  porter sandbox volume list
  ```

  ```bash JSON Output theme={null}
  porter sandbox volume list --json
  ```

  ```bash Script Volume Names theme={null}
  porter sandbox volume list --json | jq -r '.[].name'
  ```

  ```bash Ready Volume Names theme={null}
  porter sandbox volume list | awk -F'\t' '$2=="ready" {print $1}'
  ```
</CodeGroup>

### `porter sandbox volume create`

Creates a persistent volume on the current cluster. The volume name may contain lowercase letters, numbers, and hyphens, and must start and end with a letter or number. Omit the name only for one-off volumes where you do not need stable lookup later.

Volumes start in the `pending` phase. Sandboxes that mount them wait for the underlying claim to bind, so `porter sandbox create` does not need a separate wait step.

Volume names must be unique within a cluster for the lifetime of the volume. After a volume is deleted, its name can be used again.

**Usage:**

```bash theme={null}
porter sandbox volume create [name] [flags]
```

**Options:**

| Flag     | Description                      |
| -------- | -------------------------------- |
| `--json` | Print the created volume as JSON |

<CodeGroup>
  ```bash Create Named Volume theme={null}
  porter sandbox volume create my-data
  ```

  ```bash Create Generated Name theme={null}
  porter sandbox volume create
  ```

  ```bash JSON Output theme={null}
  porter sandbox volume create my-data --json
  ```
</CodeGroup>

### `porter sandbox volume get`

Shows a single sandbox volume by name, including phase, creation time, and currently attached sandboxes.

**Usage:**

```bash theme={null}
porter sandbox volume get <name> [flags]
```

**Options:**

| Flag     | Description              |
| -------- | ------------------------ |
| `--json` | Print the volume as JSON |

<CodeGroup>
  ```bash Get by Name theme={null}
  porter sandbox volume get my-data
  ```

  ```bash JSON Output theme={null}
  porter sandbox volume get my-data --json
  ```
</CodeGroup>

### `porter sandbox volume files`

Lists the files and directories under a path inside a volume. Omit the path to list from the volume root. The server walks the tree up to an entry budget; directories it could not fully read are marked truncated. List them directly to see more.

**Usage:**

```bash theme={null}
porter sandbox volume files <name|id> [path] [flags]
```

**Options:**

| Flag       | Description                                                             |
| ---------- | ----------------------------------------------------------------------- |
| `--search` | Only show entries whose name contains this substring (case-insensitive) |
| `--json`   | Print the full nested listing as JSON                                   |

<CodeGroup>
  ```bash List From Root theme={null}
  porter sandbox volume files my-data
  ```

  ```bash List a Subdirectory theme={null}
  porter sandbox volume files my-data models/checkpoints
  ```

  ```bash Filter by Name theme={null}
  porter sandbox volume files my-data --search .bin
  ```

  ```bash JSON Output theme={null}
  porter sandbox volume files my-data --json | jq '.entries[].name'
  ```
</CodeGroup>

### `porter sandbox volume read`

Streams a file's raw bytes from a volume to stdout. Redirect stdout to save the file locally, or use `--range` to read part of a large file.

**Usage:**

```bash theme={null}
porter sandbox volume read <name|id> <path> [flags]
```

**Options:**

| Flag      | Description                                                                                 |
| --------- | ------------------------------------------------------------------------------------------- |
| `--range` | Single byte range to read (`start-end`, `start-`, or `-length`). Defaults to the whole file |

Reading a path that does not exist fails with a not-found error.

<CodeGroup>
  ```bash Print a File theme={null}
  porter sandbox volume read my-data config/app.yaml
  ```

  ```bash Save Locally theme={null}
  porter sandbox volume read my-data models/model.bin > model.bin
  ```

  ```bash Read a Byte Range theme={null}
  porter sandbox volume read my-data logs/app.log --range 0-4095
  ```
</CodeGroup>

### `porter sandbox volume write`

Uploads a file into a volume at a path relative to the volume root. Parent directories are created as needed, and an existing file at the path is replaced. Content comes from stdin unless `--file` names a local file.

The write is staged to a temporary file and renamed into place, so a failed or interrupted upload leaves the previous contents alone rather than a truncated file. You can write to a volume whether or not a sandbox has it mounted.

**Usage:**

```bash theme={null}
porter sandbox volume write <name|id> <path> [flags]
```

**Options:**

| Flag     | Description                                     |
| -------- | ----------------------------------------------- |
| `--file` | Local file to upload. Defaults to reading stdin |

A single write is capped at 1 GiB, and a request from outside the cluster must finish within 30 seconds; write very large files from inside a sandbox that mounts the volume.

<CodeGroup>
  ```bash Upload a Local File theme={null}
  porter sandbox volume write my-data config/app.yaml --file ./app.yaml
  ```

  ```bash Redirect a File to stdin theme={null}
  porter sandbox volume write my-data config/app.yaml < app.yaml
  ```

  ```bash Pipe Content theme={null}
  echo hello | porter sandbox volume write my-data notes.txt
  ```
</CodeGroup>

### `porter sandbox volume move`

Moves a file or directory to a new path inside one volume. The destination is the entry's full new path, not a directory to drop it into, so a move renames and relocates in one call. A directory moves with everything under it.

**Usage:**

```bash theme={null}
porter sandbox volume move <name|id> <from> <to>
```

The destination's parent directory must already exist, and nothing is replaced. The server refuses a move onto an occupied path, and the volume root cannot be moved or overwritten.

<CodeGroup>
  ```bash Rename a File theme={null}
  porter sandbox volume move my-data notes.txt archive/notes.txt
  ```

  ```bash Move a Directory theme={null}
  porter sandbox volume move my-data models/v1 models/v2
  ```
</CodeGroup>

### `porter sandbox volume delete`

Deletes a sandbox volume by name.

**Usage:**

```bash theme={null}
porter sandbox volume delete <name>
```

The server rejects deletion while the volume is attached to any sandbox. Terminate or recreate the attached sandboxes first.

<CodeGroup>
  ```bash Delete by Name theme={null}
  porter sandbox volume delete my-data
  ```
</CodeGroup>

## Common Workflows

| Workflow                               | Command                                                                                                          |
| -------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| Create a named sandbox                 | `porter sandbox create alpine:3.20 --name web -- sleep 3600`                                                     |
| Create a sandbox that cleans itself up | `porter sandbox create alpine:3.20 --ttl 2h -- sleep 7200`                                                       |
| Create a sandbox with an env group     | `porter sandbox create alpine:3.20 --env-group my-env-group -- sleep 3600`                                       |
| Create and mount a volume              | `porter sandbox volume create my-data && porter sandbox create ubuntu:24.04 --volume /workspace=my-data -- bash` |
| Upload a local file to a volume        | `porter sandbox volume write my-data config/app.yaml --file ./app.yaml`                                          |
| Read a file back from a volume         | `porter sandbox volume read my-data config/app.yaml`                                                             |
| List running sandboxes                 | `porter sandbox list --phase running`                                                                            |
| Get running sandbox names for a script | `porter sandbox list --json \| jq -r '.[] \| select(.phase == "running") \| .name'`                              |
| Run a smoke command                    | `porter sandbox exec <sandbox-name> -- python -c 'print("ok")'`                                                  |
| Fetch recent errors                    | `porter sandbox logs <sandbox-name> --since 30m --level error`                                                   |
| Check CPU and memory usage             | `porter sandbox metrics <sandbox-name> --since 30m`                                                              |
| Preview tagged cleanup                 | `porter sandbox terminate --all --tag run=<run-id> --dry-run`                                                    |
| Clean up tagged sandboxes              | `porter sandbox terminate --all --tag run=<run-id>`                                                              |

## Related Docs

* [Sandboxes Overview](/sandboxes/overview)
* [Sandboxes Getting Started](/sandboxes/getting-started)
* [Python Sandbox SDK quickstart](/sandboxes/sdk/python/quickstart)
* [TypeScript Sandbox SDK quickstart](/sandboxes/sdk/typescript/quickstart)
