Sandboxes

Run isolated commands and manage sessions, files, and HTTP services from Python.

Run a command from trusted backend code in an environment with Sandbox access enabled:

import os
from uuid import uuid4
from volcano_sdk import VolcanoClient

client = VolcanoClient(
    anon_key=os.environ["VOLCANO_ANON_KEY"],
    service_key=os.environ["VOLCANO_SERVICE_KEY"],
    api_url=os.environ.get("VOLCANO_API_URL", "https://api.volcano.dev"),
)
project_id = os.environ["VOLCANO_PROJECT_ID"]
request_id = str(uuid4())
result = client.sandboxes.exec(
    project_id,
    'python -c "print(42)"',
    region="us-east-1",
    preset="python3.12",
    request_id=request_id,
)
print(result.stdout, result.exit_code)

Keep the same request_id when retrying an uncertain create or execution. A new ID represents a new operation. The SDK does not replay commands after transport failures. A rejected user token is refreshed once; the retry preserves the request ID. Nonzero command exits are result fields. A session command reports its timeout through timed_out. One-shot client.sandboxes.exec() instead raises ServerError(status=504) when execution times out; retrying the same request_id then raises ConflictError(status=409) because the outcome is unknown. Do not allocate a new request ID to blindly replay an execution with an unknown outcome. API failures raise typed exceptions such as ConflictError or RateLimitedError; these preserve status, code, and retry_after when supplied by the server.

Keep a session

import time

with client.sandboxes.create(
    project_id,
    region="us-east-1",
    preset="python3.12",
    max_duration_seconds=300,
) as session:
    for _ in range(60):
        if session.refresh().state == "running":
            break
        time.sleep(1)
    else:
        raise TimeoutError("Sandbox did not become ready")
    session.files.write("/workspace/input.bin", bytes(range(256)))
    assert session.files.read("/workspace/input.bin") == bytes(range(256))
    result = session.exec("wc -c /workspace/input.bin")
    print(result.stdout)

Creation, suspension, resumption, and termination are asynchronous. Use refresh() to observe state. Context exit requests termination even when the body raises; it does not wait for termination to complete. Use get(session_id) to reconnect, then suspend(), resume(), or terminate() as needed. File reads return bytes; writes accept at most 8 MiB.

Context exit skips an already terminating or terminated session and treats a cleanup 404 as already removed. Other cleanup failures preserve an existing body exception and attach a note. Session identity, lifecycle state, expiry, and file access are read-only properties. state is a SandboxState literal value; session.expires_at and access.expires_at are timezone-aware datetime values.

Access a background HTTP service

Inside a running session, detach the service and redirect its streams:

session.exec(
    "nohup python -m http.server 8080 --bind 0.0.0.0 >/tmp/http.log 2>&1 </dev/null &"
)
access = session.access(8080)
# Send access.token in X-Volcano-Sandbox-Token when requesting access.url.

The process lasts until it exits or its session ends. Access credentials are scoped to the session and port and expire at access.expires_at. Do not log the token or put it in URLs. See Sandbox HTTP access.

Select presets and authorize users

client.sandboxes.presets() lists available presets and regions. Supply exactly one of preset or a saved template's sandbox_id. Omit memory_mb to preserve that template's configured memory.

Only trusted backend code should call client.sandboxes.grant(session_id, auth_user_id, expires_at) or client.sandboxes.revoke(session_id, auth_user_id). Project users can access only sessions explicitly granted to them; they cannot create or manage sessions. Service keys stay on the backend. Anonymous keys alone cannot access sessions.

Grant expiry must be a timezone-aware datetime:

from datetime import UTC, datetime, timedelta

client.sandboxes.grant(session.id, auth_user_id, datetime.now(UTC) + timedelta(hours=1))

Management operations use the service key. Handles returned by create() retain that management scope for reads, commands, files, and HTTP access, even if the client signs in or switches users. Handles returned by get() use the current signed-in user's grant when available. Public preset discovery requires no service key or signed-in session.

Custom image deployments

Upload a tar.gz source archive containing a root Dockerfile fragment. Volcano supplies the base image; use RUN, COPY, and CMD without FROM, USER, or ENTRYPOINT.

from pathlib import Path
from uuid import uuid4

sandbox_id = str(uuid4())
request_id = str(uuid4())
deployment = client.sandboxes.deploy(
    project_id,
    sandbox_id,
    Path("source.tar.gz").read_bytes(),
    name="custom-image",
    memory_mb=1024,
    ports=[8080],
    request_id=request_id,
)
state = client.sandboxes.deployment(project_id, sandbox_id, deployment.id)

Retain both IDs, the source bytes, and build options when retrying a request. Wait for status active before creating sessions with this sandbox_id. Redeploy with the same template ID and a new request ID to update the image; existing sessions retain their original image.

deployments(project_id, sandbox_id, cursor=..., limit=10) returns history and a next_cursor. source(project_id, sandbox_id, deployment_id) returns the original archive bytes. logs(project_id, sandbox_id, deployment_id, region="aws-us-east-1", cursor=..., limit=100) returns regional build messages. delete_template(project_id, sandbox_id) deletes the template and terminates its sessions and requires the sandboxes.terminate permission. History page limits range from 1 to 100. Archives are limited to 32 MiB compressed and expanded.

Custom deployments accept at most 16 unique ports, from 1 through 65532.

Runtime billing

Confirmed cloud running time is measured using baseline memory times observed running duration and consumes the account's Sandbox allowance. Paid overage is purchased in 5 GBh blocks at 25 credits per block; unused purchased capacity carries forward. Usage is not rounded up to a whole hour per session. Suspended and uncertain time are uncharged, snapshot costs are included, and burst resources are not billed separately. Local sessions consume no cloud allowance or credits.

Plan and allowance refusals use sandbox_plan_limit_exceeded and sandbox_allowance_exhausted. Retrying these errors with a fresh request ID does not bypass the plan or account limit.

On this page