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.
This guide covers sandbox configuration options, including resources, mounted files, ports, annotations, secrets, and timeouts.
Overview
Sandbox configuration can be set in three places:
- SandboxDefaults - Shared defaults for all sandboxes in a session
- Sandbox.run() kwargs - Per-sandbox overrides
- @session.function() kwargs - Function-specific configuration
from cwsandbox import ResourceOptions, Sandbox, SandboxDefaults, Session
# Via SandboxDefaults
defaults = SandboxDefaults(
container_image="python:3.11",
max_lifetime_seconds=3600,
resources=ResourceOptions(
requests={"cpu": "500m", "memory": "512Mi"},
limits={"cpu": "2", "memory": "2Gi"},
),
)
# Via Sandbox.run() kwargs
sandbox = Sandbox.run(
defaults=defaults,
resources=ResourceOptions(
requests={"cpu": "1", "memory": "1Gi"},
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
Resources
Configure CPU, memory, and GPU resources using the ResourceOptions dataclass or a plain dict:
from cwsandbox import ResourceOptions, Sandbox
# Using ResourceOptions
sandbox = Sandbox.run(
resources=ResourceOptions(
requests={"cpu": "500m", "memory": "512Mi"},
limits={"cpu": "2", "memory": "2Gi"},
),
)
# Using dict
sandbox = Sandbox.run(
resources={
"requests": {"cpu": "500m", "memory": "512Mi"},
"limits": {"cpu": "2", "memory": "2Gi"},
},
)
Both forms are equivalent: dicts are automatically converted 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, 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 and default to None, which uses backend defaults.
Guaranteed QoS
When requests equal limits, the sandbox gets a Guaranteed Quality of Service class. This reserves exact resources and prevents throttling. Use this for latency-sensitive workloads.
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:
# Flat dict shorthand: requests and limits are set to identical values
sandbox = Sandbox.run(
resources={"cpu": "2", "memory": "4Gi"},
)
Burstable QoS
When requests are lower than limits, the sandbox gets a Burstable Quality of Service class. Lower requests let the scheduler bin-pack more sandboxes, but each sandbox can burst up to its limit if capacity is available.
sandbox = Sandbox.run(
resources=ResourceOptions(
requests={"cpu": "500m", "memory": "512Mi"},
limits={"cpu": "4", "memory": "4Gi"},
),
)
CPU values
CPU is specified in millicores or whole cores. See Resource units in Kubernetes for details.
| 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:
| Value | Meaning |
|---|
"128Mi" | 128 mebibytes |
"512Mi" | 512 mebibytes |
"1Gi" | 1 gibibyte |
"4Gi" | 4 gibibytes |
GPU
Request GPU resources alongside CPU and memory.
sandbox = Sandbox.run(
resources=ResourceOptions(
requests={"cpu": "4", "memory": "16Gi"},
limits={"cpu": "8", "memory": "32Gi"},
gpu={"count": 1, "type": "H100"},
),
)
GPU configuration keys:
| Key | Type | Description |
|---|
count | int | Number of GPUs to request. |
type | str | GPU type (for example, "H100", "B200"). |
memory_gb | int | GPU memory in GB (optional). |
Inspecting confirmed resources
After a sandbox starts, inspect the confirmed resource allocation:
with Sandbox.run(
resources=ResourceOptions(
requests={"cpu": "500m", "memory": "512Mi"},
limits={"cpu": "2", "memory": "2Gi"},
gpu={"count": 1, "type": "H100"},
),
) as sb:
print(sb.resource_requests) # {"cpu": "500m", "memory": "512Mi"}
print(sb.resource_limits) # {"cpu": "2", "memory": "2Gi"}
print(sb.resource_gpu) # {"count": 1, "type": "H100"}
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.
# sandbox.yaml
container_image: "pytorch/pytorch:2.4.0-cuda12.4-cudnn9-runtime"
max_lifetime_seconds: 3600
tags:
- training
- experiment-42
resources:
requests:
cpu: "1"
memory: "2Gi"
limits:
cpu: "4"
memory: "8Gi"
gpu:
count: 1
type: "H100"
network:
egress_mode: "internet"
environment_variables:
LOG_LEVEL: "info"
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 passed 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
Mount files into the sandbox at startup:
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 need modification.
Ports
Expose ports from the sandbox:
sandbox = Sandbox.run(
"python", "-m", "http.server", "8080",
ports=[
{"container_port": 8080},
],
)
Port configuration
| Field | Type | Description |
|---|
container_port | int | Port inside the sandbox |
Network
Configure network options using the NetworkOptions dataclass or a plain dict:
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: dicts are automatically converted to NetworkOptions internally.
NetworkOptions fields
| Field | Type | Description |
|---|
ingress_mode | str | None | Controls inbound traffic: "public" (internet accessible), "internal" (cluster only), etc. |
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: "internet" (full access), "isolated" (no external), "org" (org-internal only), etc. |
All fields are optional and default to None, which uses backend defaults.
Setting network in SandboxDefaults
Set a default network configuration for all sandboxes:
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.
sandbox = Sandbox.run(
annotations={
"team": "ml-infra",
"experiment": "training-run-42",
},
)
Session-level defaults
Set default annotations for all sandboxes in a session:
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 gets: team=ml-research, managed-by=sandbox-sdk, experiment=run-42
Merge behavior
When both SandboxDefaults.annotations and per-sandbox annotations are provided, they are merged. Explicit per-sandbox values win on key collision, matching the same semantics as environment_variables.
SUNK integration
For passing Slurm context as pod annotations for SUNK integration, see the SUNK Pod Scheduler integration guide.
Validation
The SDK does not validate annotation keys or values. The server handles validation of reserved key prefixes, value format, and maximum entry count.
Secrets
Inject secrets from secret stores as environment variables using the Secret type:
from cwsandbox import Sandbox, Secret
with Sandbox.run(
"pip", "install", "huggingface_hub",
secrets=[
Secret(store="wandb", name="HF_TOKEN"),
],
) as sandbox:
sandbox.wait()
result = sandbox.exec([
"python", "-c",
"from huggingface_hub import whoami; print(whoami()['name'])",
]).result()
print(result.stdout.strip()) # Your Hugging Face username
Unlike environment_variables, secrets are never passed in plaintext. They are resolved server-side from the named store and injected securely.
Secret stores are configured at the organization level by administrators. Stores connect to external providers (for example, W&B Secret Manager) and are resolved server-side at sandbox creation. Stores are independent of client-side authentication.
Secret fields
| Field | Type | Default | Description |
|---|
store | str | required | Secret store configured for your organization (for example, "wandb"). |
name | str | required | Name of the secret in the store. |
field | str | "" | Specific field within a structured secret. |
env_var | str | same as name | Environment variable the secret is injected as. |
Common patterns
from cwsandbox import Secret
# Minimal: env_var defaults to name
Secret(store="wandb", name="HF_TOKEN")
# -> injected as HF_TOKEN
# Custom env_var name
Secret(store="wandb", name="HF_TOKEN", env_var="HUGGINGFACE_TOKEN")
# -> injected as HUGGINGFACE_TOKEN
# Extracting a field from a structured secret
Secret(store="wandb", name="db-credentials", field="password", env_var="DB_PASS")
# -> injected as DB_PASS
Setting secrets in SandboxDefaults
Share secrets across all sandboxes in a session:
from cwsandbox import SandboxDefaults, Secret, Session
defaults = SandboxDefaults(
secrets=(
Secret(store="wandb", name="HF_TOKEN"),
Secret(store="wandb", name="OPENAI_API_KEY"),
),
)
with Session(defaults) as session:
# All sandboxes get both secrets
sb1 = session.sandbox()
sb2 = session.sandbox()
# Add more secrets for a specific sandbox
sb3 = session.sandbox(
secrets=[Secret(store="vault", name="DB_PASS")],
)
# sb3 gets all three secrets (defaults + explicit merged)
Duplicate detection
If two secrets target the same env_var but differ in store, name, or field, a ValueError is raised at sandbox creation time. Identical duplicates (same store, name, field, env_var) are allowed silently:
# Raises ValueError: conflicting secrets for env_var 'HF_TOKEN'
Sandbox.run(
defaults=SandboxDefaults(
secrets=(Secret(store="wandb", name="HF_TOKEN"),),
),
secrets=[Secret(store="vault", name="hf-token", env_var="HF_TOKEN")],
)
Timeouts
timeout_seconds
Per-command timeout:
# Per-exec timeout
result = sandbox.exec(
["python", "long_script.py"],
timeout_seconds=300, # 5 minute timeout
).result()
This controls how long the client waits for a response. If exceeded, raises SandboxTimeoutError.
max_lifetime_seconds
Maximum sandbox lifetime (set in SandboxDefaults):
defaults = SandboxDefaults(
container_image="python:3.11",
max_lifetime_seconds=3600, # 1 hour max lifetime
)
with Sandbox.run(defaults=defaults) as sandbox:
# Sandbox automatically terminates after 1 hour
pass
This is a server-side limit. The sandbox will be terminated when it reaches this age, regardless of activity.
Complete example
from cwsandbox import NetworkOptions, ResourceOptions, Sandbox, SandboxDefaults, Secret
defaults = SandboxDefaults(
container_image="python:3.11",
max_lifetime_seconds=1800, # 30 minutes
tags=("production", "ml-pipeline"),
resources=ResourceOptions(
requests={"cpu": "1", "memory": "2Gi"},
limits={"cpu": "4", "memory": "8Gi"},
),
network=NetworkOptions(egress_mode="internet"),
secrets=(Secret(store="wandb", name="HF_TOKEN"),),
)
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)