Skip to main content
The Python Sandbox SDK is published as porter-sandbox. Use it from application code running in your sandbox-enabled Porter cluster.
Sandboxes are in a private beta. Please reach out to us at support@porter.run or over Slack if you are interested in joining.

Install

Add the SDK to your application image:
pip install porter-sandbox
Or with uv:
uv add porter-sandbox

Create your first sandbox

Create a sandbox, execute a command, print the command output, and terminate the sandbox when the work is done:
from porter_sandbox import Porter

with Porter() as porter:
    sandbox = porter.sandboxes.create(
        image="python:3.12-slim",
        name="python-quickstart",
        tags={"example": "python-quickstart"},
    )

    result = sandbox.exec(["python", "-c", "print('hello from porter')"])
    print(result.stdout)

    sandbox.terminate()
The SDK connects to the in-cluster Sandbox API automatically when this code runs as a Porter Application in the same cluster where sandboxes are enabled. If you need to invoke sandboxes from outside that cluster, set PORTER_SANDBOX_API_KEY to a Porter API token and PORTER_CLUSTER_ID to the target cluster. The SDK reads the project from the token and builds the external Porter API URL automatically; see calling from outside the cluster for details. Use sandbox names when you need to fetch, inspect, exec into, or terminate a sandbox later. Sandbox names must be unique within a cluster and currently cannot be reused, even after the sandbox is terminated.

Fetch logs

Logs are returned as structured log lines:
from porter_sandbox import Porter

with Porter() as porter:
    sandbox = porter.sandboxes.create(
        image="python:3.12-slim",
        name="python-logs-demo",
    )

    try:
        sandbox.exec(["python", "-c", "print('log me')"])

        for line in sandbox.logs(limit=100):
            print(f"{line.timestamp} [{line.level}] {line.line}")
    finally:
        sandbox.terminate()

Get a sandbox by name

Use get(name) when you know the sandbox name:
from porter_sandbox import Porter

with Porter() as porter:
    sandbox = porter.sandboxes.get("python-quickstart")
    print(sandbox.phase)

List sandboxes

Use tags to find sandboxes created by a workflow:
from porter_sandbox import Porter

with Porter() as porter:
    sandboxes = porter.sandboxes.list(tags={"workflow": "agent-run"})

    for sandbox in sandboxes:
        print(sandbox.phase)

Async usage

Use AsyncPorter from async application code:
import asyncio
from porter_sandbox import AsyncPorter

async def main():
    async with AsyncPorter() as porter:
        sandbox = await porter.sandboxes.create(
            image="python:3.12-slim",
            name="async-python-demo",
        )

        try:
            result = await sandbox.exec(["python", "-c", "print(2 + 2)"])
            print(result.stdout)
        finally:
            await sandbox.terminate()

asyncio.run(main())
Launch many sandboxes concurrently with asyncio.gather:
async with AsyncPorter() as porter:
    sandboxes = await asyncio.gather(*[
        porter.sandboxes.create(
            image="python:3.12-slim",
            name=f"batch-demo-{i}",
            tags={"batch": "demo"},
        )
        for i in range(10)
    ])

Next steps