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

# 샌드박스 설정

> 샌드박스의 리소스, 이미지, 네트워킹, timeout, 저장소를 설정합니다.

<Note>
  The examples on this page omit `auth`. To authenticate with your W\&B API key, install `cwsandbox[wandb]` and pass `auth=AuthStrategy.WANDB` to `Sandbox.run()`, `Sandbox()`, or `Sandbox.session()`. See [Get started](/products/sandboxes/serverless/get-started).
</Note>

This guide covers sandbox configuration options, including resources, mounted files, ports, annotations, and timeouts. Use it as a reference when you need to tailor a sandbox's runtime environment to match the requirements of a workload, such as reserving GPU capacity, exposing a service, or setting operation timeouts.

This page is for developers who use the `cwsandbox` Python SDK to launch and manage sandboxes.

## Overview

You can set sandbox configuration in three places, listed from broadest to most specific scope:

* **SandboxDefaults**: Shared defaults for all sandboxes in a session.
* **Sandbox.run() kwargs**: Per-sandbox overrides.
* **@session.function() kwargs**: Function-specific configuration.

```python theme={"system"}
from cwsandbox import ResourceOptions, Sandbox, SandboxDefaults, Session

# Via SandboxDefaults
defaults = SandboxDefaults(
    container_image="python:3.11",
    max_lifetime_seconds=3600,
    resources=ResourceOptions(
        requests={"cpu": "2", "memory": "2Gi"},
        limits={"cpu": "2", "memory": "2Gi"},
    ),
)

# Via Sandbox.run() kwargs
sandbox = Sandbox.run(
    defaults=defaults,
    resources=ResourceOptions(
        requests={"cpu": "4", "memory": "4Gi"},
        limits={"cpu": "4", "memory": "4Gi"},
    ),
)

# Via @session.function() kwargs
with Session(defaults) as session:
    @session.function(resources={"cpu": "1", "memory": "1Gi"})
    def compute(x: int) -> int:
        return x * 2
```

The following sections describe each configuration area in detail.

## Resources

`ResourceOptions` 데이터 클래스나 일반 dict를 사용해 CPU, 메모리, GPU 리소스를 설정하세요. 서버리스 배치에서는 requests와 limits가 같은 값으로 적용됩니다.

The following examples use equal requests and limits:

```python theme={"system"}
from cwsandbox import ResourceOptions, Sandbox

# Using ResourceOptions
sandbox = Sandbox.run(
    resources=ResourceOptions(
        requests={"cpu": "2", "memory": "2Gi"},
        limits={"cpu": "2", "memory": "2Gi"},
    ),
)

# Using dict
sandbox = Sandbox.run(
    resources={
        "requests": {"cpu": "2", "memory": "2Gi"},
        "limits": {"cpu": "2", "memory": "2Gi"},
    },
)
```

Both forms are equivalent: the SDK automatically converts dicts to `ResourceOptions` internally.

`ResourceOptions` separates resource requests from limits. Requests tell the scheduler what the sandbox needs. Limits set the maximum it can use. For background on how Kubernetes uses requests and limits to assign [Quality of Service classes](https://kubernetes.io/docs/tasks/configure-pod-container/quality-service-pod/), see the Kubernetes documentation.

### ResourceOptions fields

| Field | Type | Description |
| - | - | - |
| `requests` | `dict[str, str] \| None` | CPU and memory requests for scheduling (for example, `{"cpu": "500m", "memory": "512Mi"}`). |
| `limits` | `dict[str, str] \| None` | CPU and memory limits the sandbox cannot exceed (for example, `{"cpu": "2", "memory": "2Gi"}`). |
| `gpu` | `dict[str, Any] \| None` | GPU configuration (for example, `{"count": 1, "type": "H100"}`). |

All fields are optional in the client and default to `None`. The resolved allocation must satisfy the applicable policy. If you omit the resource allocation, complete, positive CPU and memory defaults from that policy supply both requests and limits.

### Guaranteed QoS

When requests equal limits, the sandbox receives a Guaranteed Quality of Service class. The scheduler reserves the full allocation, and Kubernetes evicts Guaranteed Pods last under node resource pressure. A sandbox that tries to use more CPU than its limit is still throttled. Use this for latency-sensitive workloads.

```python theme={"system"}
sandbox = Sandbox.run(
    resources=ResourceOptions(
        requests={"cpu": "2", "memory": "4Gi"},
        limits={"cpu": "2", "memory": "4Gi"},
    ),
)
```

For Guaranteed QoS, a flat dict shorthand is available. The SDK normalizes flat dicts by setting both requests and limits to the same values.

```python theme={"system"}
# Flat dict shorthand: requests and limits are set to identical values
sandbox = Sandbox.run(
    resources={"cpu": "2", "memory": "4Gi"},
)
```

### CPU values

Specify CPU in millicores or whole cores. For details, see [Resource units in Kubernetes](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#resource-units-in-kubernetes).

| Value | Meaning |
| - | - |
| `"100m"` | 100 millicores (0.1 CPU) |
| `"500m"` | 500 millicores (0.5 CPU) |
| `"1000m"` or `"1"` | 1 full CPU core |
| `"2000m"` or `"2"` | 2 CPU cores |

### Memory values

Memory uses standard [Kubernetes memory units](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#meaning-of-memory):

| Value | Meaning |
| - | - |
| `"128Mi"` | 128 mebibytes |
| `"512Mi"` | 512 mebibytes |
| `"1Gi"` | 1 gibibyte |
| `"4Gi"` | 4 gibibytes |

### GPU

Request GPU resources alongside CPU and memory. A GPU request reserves GPUs only, so set CPU and memory explicitly.

```python theme={"system"}
sandbox = Sandbox.run(
    resources=ResourceOptions(
        requests={"cpu": "4", "memory": "16Gi"},
        limits={"cpu": "4", "memory": "16Gi"},
        gpu={"count": 1},
    ),
)
```

GPU configuration keys:

| 키 | 유형 | 설명 |
| - | - | - |
| `count` | `int` | 요청할 GPU 수입니다. |
| `type` | `str` | GPU 유형입니다. 서버리스 용량에서는 GPU 유형이 자동으로 부여되므로 설정하지 않은 상태로 두세요. [GPU 샌드박스 실행](/ko/products/sandboxes/serverless/gpu-sandboxes)을 참조하세요. |
| `memory_gb` | `int` | 최소 GPU 메모리(GB)입니다. 러너에서 이 값 이상의 메모리를 갖춘 GPU 유형을 선택합니다. 조건을 충족하는 유형이 여러 개인 경우 어떤 유형이 선택될지는 보장되지 않습니다. |

`type` and `memory_gb` are alternatives. Set at most one of them.

### Configuration library interop

All configuration types (`NetworkOptions`, `Secret`, `ResourceOptions`) accept either the dataclass or a plain dict. This means ML configuration libraries that resolve configs to dicts or dict-like objects can pass values directly to the SDK without manual conversion.

`SandboxDefaults.from_dict()` accepts a plain dict or an OmegaConf `DictConfig` and coerces nested fields automatically. Dicts become `NetworkOptions`, `Secret`, or `ResourceOptions` as needed, and lists become tuples.

```yaml theme={"system"}
# sandbox.yaml
container_image: "pytorch/pytorch:2.4.0-cuda12.4-cudnn9-runtime"
max_lifetime_seconds: 3600
tags:
  - training
  - experiment-42
resources:
  requests:
    cpu: "4"
    memory: "8Gi"
  limits:
    cpu: "4"
    memory: "8Gi"
  gpu:
    count: 1
network:
  egress_mode: "internet"
environment_variables:
  LOG_LEVEL: "info"
```

```python theme={"system"}
from omegaconf import OmegaConf
from cwsandbox import Sandbox, SandboxDefaults

cfg = OmegaConf.load("sandbox.yaml")
defaults = SandboxDefaults.from_dict(cfg)

with Sandbox.run(defaults=defaults) as sb:
    result = sb.exec(["python", "train.py"]).result()
```

Individual fields also accept dicts when you pass them directly to `Sandbox.run()` or `session.sandbox()`. The nested dict form for resources maps to `ResourceOptions` fields. The flat dict form (`{"cpu": "1", "memory": "1Gi"}`) is also accepted and treated as Guaranteed QoS.

## Mounted files

Mounted files let you provide configuration files, scripts, or other read-only assets to the sandbox at startup, without baking them into a container image.

```python theme={"system"}
sandbox = Sandbox.run(
    mounted_files=[
        {
            "path": "/app/config.json",
            "content": '{"debug": true}',
        },
        {
            "path": "/app/script.py",
            "content": "print('hello')",
        },
    ],
)

# Files are available immediately
result = sandbox.exec(["python", "/app/script.py"]).result()
```

### Mount options

| Field | Type | Description |
| - | - | - |
| `path` | `str` | Absolute path in sandbox. |
| `content` | `str` | File content (text). |

Mounted files are read-only. Use `write_file()` for files that require modification.

## Ports

Expose ports so processes inside the sandbox can serve traffic to outside clients:

```python theme={"system"}
sandbox = Sandbox.run(
    "python", "-m", "http.server", "8080",
    ports=[
        {"container_port": 8080},
    ],
)
```

### Port configuration

| Field | Type | Description |
| - | - | - |
| `container_port` | `int` | Port inside the sandbox. |

To give a port an internet-reachable address with platform TLS or TLS passthrough, see [Public endpoints](../../public-endpoints).

## Network

Configure network options using the `NetworkOptions` dataclass or a plain dict:

```python theme={"system"}
from cwsandbox import NetworkOptions, Sandbox

# Using NetworkOptions
sandbox = Sandbox.run(
    network=NetworkOptions(
        ingress_mode="public",
        exposed_ports=(8080,),
    ),
)

# Using dict
sandbox = Sandbox.run(
    network={"ingress_mode": "public", "exposed_ports": [8080]},
)
```

Both forms are equivalent: the SDK automatically converts dicts to `NetworkOptions` internally.

### NetworkOptions fields

| Field | Type | Description |
| - | - | - |
| `ingress_mode` | `str \| None` | Controls inbound traffic, such as `"public"` (internet accessible) or `"internal"` (cluster only). |
| `exposed_ports` | `tuple[int, ...] \| None` | Ports to expose (required with `ingress_mode`). Pass as tuple `(8080,)` or list `[8080]`. |
| `egress_mode` | `str \| None` | Controls outbound traffic, such as `"internet"` (full access), `"isolated"` (no external), or `"org"` (org-internal only). |

All fields are optional and default to `None`, which uses backend defaults.

### Set network in SandboxDefaults

Set a default network configuration for all sandboxes:

```python theme={"system"}
from cwsandbox import NetworkOptions, SandboxDefaults, Session

defaults = SandboxDefaults(
    network=NetworkOptions(egress_mode="internet"),
)

with Session(defaults) as session:
    # All sandboxes inherit the network config
    sb1 = session.sandbox()  # Uses egress_mode="internet"
    sb2 = session.sandbox()  # Uses egress_mode="internet"

    # Override for specific sandbox
    sb3 = session.sandbox(network=NetworkOptions(egress_mode="user"))
```

## Annotations

Add Kubernetes pod annotations to sandboxes. Annotations are key-value string pairs attached to the underlying pod, useful for integrations that read pod metadata, such as schedulers, cost-allocation tools, or external automation.

```python theme={"system"}
sandbox = Sandbox.run(
    annotations={
        "team": "ml-infra",
        "experiment": "training-run-42",
    },
)
```

### Session-level defaults

Set default annotations for all sandboxes in a session:

```python theme={"system"}
defaults = SandboxDefaults(
    annotations={
        "team": "ml-infra",
        "managed-by": "sandbox-sdk",
    },
)

with Session(defaults) as session:
    # Inherits default annotations
    sb1 = session.sandbox()

    # Merge: explicit annotations override defaults on key collision
    sb2 = session.sandbox(
        annotations={"experiment": "run-42", "team": "ml-research"},
    )
    # sb2 receives: team=ml-research, managed-by=sandbox-sdk, experiment=run-42
```

### Merge behavior

When you provide both `SandboxDefaults.annotations` and per-sandbox `annotations`, the SDK merges them. Explicit per-sandbox values win on key collision, matching the same semantics as `environment_variables`.

### Validation

The SDK does not validate annotation keys or values. The server handles validation of reserved key prefixes, value format, and maximum entry count.

<h2 id="secrets">
  시크릿
</h2>

W\&B 팀 시크릿 주입, 인증 요구 사항, Python 및 TypeScript 예시는 [W\&B 시크릿 사용](/ko/products/sandboxes/serverless/secrets)을 참조하세요.

## Timeouts

Four settings control how long a sandbox and its operations run. The names are similar, so the following table maps each one to what it bounds.

| Setting | Bounds | Enforced by | Default |
| - | - | - | - |
| `max_lifetime_seconds` | Total sandbox lifetime before automatic termination | Server | Runner-policy default, or 10 minutes if none is set |
| `timeout_seconds` | A single `exec()`, `read_file()`, or `write_file()` call | Client | Falls back to `request_timeout_seconds` |
| `max_timeout_seconds` | How long the server waits to place a sandbox before the attempt fails | Server | 30 seconds |
| `request_timeout_seconds` | Client-side HTTP timeout for most remote procedure calls (RPCs) | Client | 300 seconds |

Only `max_lifetime_seconds` terminates a sandbox. The other three bound individual requests and leave the sandbox running.

### max\_lifetime\_seconds

`max_lifetime_seconds` sets the total wall-clock lifetime of a sandbox. When a sandbox reaches this age, the platform terminates it even if work is still in progress.

If you don't set `max_lifetime_seconds`, the runner-policy default applies. If no positive policy default supplies a lifetime, the platform uses 10 minutes. Set the value explicitly for long-running work:

```python theme={"system"}
from cwsandbox import Sandbox

with Sandbox.run(max_lifetime_seconds=7200) as sandbox:  # 2 hours
    sandbox.exec(["python", "train.py"], timeout_seconds=7000).result()
```

To apply one lifetime to every sandbox a session creates, set it in `SandboxDefaults`:

```python theme={"system"}
from cwsandbox import Sandbox, SandboxDefaults

defaults = SandboxDefaults(
    container_image="python:3.11",
    max_lifetime_seconds=3600,  # 1 hour
)

with Sandbox.run(defaults=defaults) as sandbox:
    sandbox.exec(["python", "-c", "print('working')"]).result()
```

An explicit `max_lifetime_seconds` argument on `Sandbox.run()` takes precedence over the value in `defaults`. Because the argument's default is `None`, you can't use it to remove a lifetime that `defaults` already sets.

요청할 수 있는 최대 수명은 30일(2,592,000초)입니다. 이보다 긴 수명을 요청하면 플랫폼에서 거부됩니다.

<Warning>
  You can't change `max_lifetime_seconds` after a sandbox starts. No API extends the lifetime of a running sandbox, so choose the value when you create it. To keep working past the limit, capture what you need and start a new sandbox with a lifetime of up to 30 days.
</Warning>

샌드박스 생성 예시는 [장기 실행 샌드박스 실행](/ko/products/sandboxes/serverless/long-running-sandboxes)을 참조하세요.

#### Behavior when the lifetime expires

The platform enforces the lifetime by terminating the sandbox's underlying Kubernetes Job, so termination is immediate rather than graceful. Two consequences matter in practice:

* Processes inside the sandbox don't receive the grace period that [`stop()`](sandbox-lifecycle) provides. Anything not already written out is lost.
* The client doesn't receive a distinct error or reason for the termination. The SDK reports no termination reason on a `Sandbox`, so you can't distinguish lifetime expiry from other failures programmatically. Track the age of long-lived sandboxes yourself.

Set a lifetime that comfortably exceeds the work you expect, and stop sandboxes explicitly when you finish rather than relying on expiry.

### timeout\_seconds

`timeout_seconds` bounds a single operation on a running sandbox:

```python theme={"system"}
result = sandbox.exec(
    ["python", "long_script.py"],
    timeout_seconds=300,  # 5 minutes
).result()
```

This controls how long the client waits for a response. If the wait exceeds the timeout, the SDK raises `SandboxTimeoutError`. The sandbox keeps running. The clock starts only after the sandbox reaches `RUNNING`, so startup time doesn't count against it.

Keep `timeout_seconds` below the sandbox's `max_lifetime_seconds`. A command whose timeout exceeds the sandbox's remaining lifetime stops when the sandbox terminates.

### max\_timeout\_seconds

`max_timeout_seconds` bounds how long the server waits to place a sandbox on a runner before the attempt fails. It's a server-side placement budget, not a client read timeout, and it doesn't affect how long the sandbox runs afterward:

```python theme={"system"}
sandbox = Sandbox.run(max_timeout_seconds=120)
```

The server accepts values from 1 to 300 seconds and defaults to 30. The server clamps values above 300 to 300. If sandboxes fail to start under heavy cluster load, raise the value. `max_timeout_seconds` is available on `Sandbox.run()` and `session.sandbox()`, but not on `SandboxDefaults`.

### request\_timeout\_seconds

`request_timeout_seconds` is the client-side HTTP timeout applied to most RPCs, and it's the fallback when a call doesn't pass its own `timeout_seconds`. Set it in `SandboxDefaults`:

```python theme={"system"}
defaults = SandboxDefaults(request_timeout_seconds=600.0)
```

Status polling uses `poll_rpc_timeout_seconds` instead, so a stalled poll fails fast rather than blocking for the full request timeout. For the full set of client-side tuning options, see the [SandboxDefaults reference](/products/sandboxes/client/ref/core/sandbox-defaults).

## Complete example

The following example combines defaults, per-sandbox overrides, mounted files, and ports into a single configuration that mirrors how a production workload might be launched:

```python theme={"system"}
from cwsandbox import NetworkOptions, ResourceOptions, Sandbox, SandboxDefaults

defaults = SandboxDefaults(
    container_image="python:3.11",
    max_lifetime_seconds=1800,  # 30 minutes
    tags=("production", "ml-pipeline"),
    resources=ResourceOptions(
        requests={"cpu": "4", "memory": "8Gi"},
        limits={"cpu": "4", "memory": "8Gi"},
    ),
    network=NetworkOptions(egress_mode="internet"),
)

with Sandbox.run(
    defaults=defaults,
    mounted_files=[
        {
            "path": "/app/config.yaml",
            "content": "model: gpt-4\nmax_tokens: 1000",
        },
    ],
    ports=[
        {"container_port": 8000},
    ],
) as sandbox:
    # Install dependencies
    sandbox.exec(["pip", "install", "fastapi", "uvicorn"]).result()

    # Run application
    result = sandbox.exec(
        ["python", "-c", "print('Server started')"],
        timeout_seconds=60,
    ).result()
    print(result.stdout)
```
