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

# Sandbox essentials

> Configure, connect to, and safely clean up Thunder sandboxes.

A sandbox is an isolated, short-lived Ubuntu VM. Its CPU, memory, disk, GPU,
environment, SSH key, network policy, and lifetime are fixed at creation.

## Configure a sandbox

```python theme={null}
import thunder

sandbox = thunder.Sandbox.create(
    name="training-run",
    cpu=4,
    memory=32,       # GiB
    storage=50,      # GiB, ephemeral
    gpu="H100",     # Omit for CPU-only
    env={"RUN_ID": "42"},
    timeout=3600,    # Lifetime in seconds
    wait_for_capacity=300,
)
```

The defaults are 4 vCPUs, 32 GiB of memory, 50 GiB of storage, no GPU, and a
300-second lifetime. Supported resource ranges and GPU types are controlled by
your organization's sandbox configuration. `wait_for_capacity` retries only a
temporary lack of the requested GPU; other errors return immediately.

<Warning>
  `timeout=None` disables automatic expiry. Keep an enforced lifetime unless
  the caller can always terminate the sandbox itself.
</Warning>

## Run commands and move files

Wait for `running` before using SSH-backed operations.

```python theme={null}
sandbox.wait_until_running(timeout=300)

process = sandbox.exec(
    "python3", "train.py",
    workdir="/home/ubuntu/project",
    env={"EPOCHS": "3"},
    timeout=600,
)
print(process.stdout.read())
exit_code = process.wait()

sandbox.upload("dataset", "/home/ubuntu/dataset", recursive=True)
sandbox.download("/home/ubuntu/results.json", "results.json")
```

Commands use the system `ssh` client; transfers use `scp`. The SSH user is
`ubuntu`. `sandbox.ssh_command` returns the exact base command for manual SSH or
port forwarding.

## Control outbound access

Public internet access is allowed by default. Private, metadata, and other
sandbox networks remain blocked, and inbound access is limited to SSH.

```python theme={null}
# No outbound internet.
closed = thunder.Sandbox.create(block_network=True)

# Restrict both destination addresses and DNS names.
restricted = thunder.Sandbox.create(
    outbound_cidr_allowlist=["203.0.113.0/24"],
    outbound_domain_allowlist=["example.com", "*.example.com"],
)
```

CIDR and domain rules are independent: when both are restricted, a connection
must pass both controls. A bare domain matches only that name; use `*.` for its
subdomains. Network policy cannot be changed after creation.

## Reconnect and list

Every sandbox has a permanent `id` and an optional `name` label. Names can be
reused after a sandbox stops, so store and use the ID.

```python theme={null}
with thunder.Client.from_cli() as client:
    sandbox = client.get_sandbox("sbx-0123456789abcdef")
    sandbox.refresh()

    for item in client.list_sandboxes():
        print(item.id, item.name, item.status.value)
```

The SDK stores generated keys under `~/.thunder/sandbox_keys/<sandbox-id>`. You
need that private key to run commands or transfer files from a later process or
machine; SSH keys cannot be replaced after creation.

## Lifecycle and cleanup

The lifecycle is `pending` → `starting` → `running` → `stopping` → `stopped`,
with `failed` as a terminal state. `Sandbox.create()` returns before startup is
complete, and `wait_until_running()` reports the failure reason if startup
fails.

```python theme={null}
sandbox = thunder.Sandbox.create(timeout=3600)
try:
    sandbox.wait_until_running()
    # Work with the sandbox.
finally:
    sandbox.terminate()
```

Billing begins when the sandbox reaches `running` and ends when it stops.
Allocated GPUs, vCPUs, memory GiB, and storage GiB are metered per second.
Expiry or `terminate()` permanently destroys the filesystem, while status and
configuration remain in sandbox history. Sandboxes cannot be restarted,
resized, or snapshotted; create a new one instead.

For asyncio applications, `AsyncSandbox` exposes the same operations as
awaitable methods.
