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, pass an API token to the SDK. You can create an API token from Settings > API tokens in the Porter Dashboard. Creating API tokens requires admin permissions. 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