Skip to main content
Porter Sandboxes are isolated container workloads that you launch from code running inside your Porter cluster. Use them for code interpreters, agent tools, batch fan-out, and other on-demand workloads that may need persistent storage through volumes.
Sandboxes are in a private beta. Please reach out to us at support@porter.run or over Slack if you are interested in joining.

Prerequisites

  • A Porter project
  • An AWS cluster where you want to run sandboxes. We recommend creating a new AWS cluster for sandboxes so sandbox workloads are isolated from your other running workloads.
A dedicated cluster is not required, it’s a defense-in-depth recommendation. Sandboxes often run untrusted code (end-user submissions, LLM agent output), and running them in their own cluster isolates that code from your main workloads as much as possible. Sharing a cluster with your other workloads is reasonable for development and testing; for production workloads that run untrusted code, we recommend the separate cluster.

Enable sandboxes

1

Enable sandboxes from the Sandbox tab

Sandboxes can only be enabled on AWS clusters.In the Porter Dashboard, navigate to the Sandbox tab for the AWS cluster where you want to run sandboxes and click Enable sandboxes.Sandbox tab with the enable sandboxes button
2

Install a Sandbox SDK in your application

Add either the Python or TypeScript Sandbox SDK to the application that will create and manage sandboxes.Sandbox tab after sandboxes are enabled
3

Write your first sandbox call

In your application code, create a sandbox, execute a command, read the output, and terminate the sandbox when the work is done. The examples below show the smallest end-to-end flow.
4

Deploy your application to the sandbox cluster

For now, we recommend deploying the application that uses the SDK as a Porter Application in the same cluster where you want to run sandboxes.

Calling from outside the cluster

The SDK connects to the in-cluster Sandbox API automatically when your application runs as a Porter Application in the same cluster where sandboxes are enabled. To invoke sandboxes from anywhere else, authenticate with a Porter API token and tell the SDK which cluster to target:
export PORTER_SANDBOX_API_KEY="<porter-api-token>"
export PORTER_CLUSTER_ID="<cluster-id>"
The SDK reads the project from the token and routes through the Porter API at dashboard.porter.run automatically. Replace <cluster-id> with the cluster where sandboxes are enabled. You can copy the prefilled snippet from the cluster’s Sandbox tab, or look up the ID with the Porter CLI:
$ porter cluster list
ID  NAME              RESOURCE_NAME     SERVER
2   my-sandbox-cluster ...
You can create an API token from Settings > API tokens in the Porter Dashboard. Creating API tokens requires admin permissions. To point the SDK at a specific URL instead, set PORTER_SANDBOX_BASE_URL or pass base_url (Python) / baseUrl (TypeScript) to the client constructor. These take precedence over everything above.

Python quickstart

Install the SDK in your application image:
pip install porter-sandbox
Create a sandbox, run a command, print the output, and terminate it:
from porter_sandbox import Porter

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

    result = sandbox.exec(["python", "-c", "print(2 + 2)"])
    print(result.stdout)

    logs = sandbox.logs(limit=100)
    print(logs)

    sandbox.terminate()

TypeScript quickstart

Install the SDK in your application image:
npm install porter-sandbox
Create a sandbox, run a command, print the output, and terminate it:
import { Porter } from "porter-sandbox";

const porter = new Porter();

const sandbox = await porter.sandboxes.create({
  image: "python:3.12-slim",
  name: "getting-started-typescript",
  tags: { example: "getting-started" },
});

const result = await sandbox.exec(["python", "-c", "print(2 + 2)"]);
console.log(result.stdout);

const logs = await sandbox.logs({ limit: 100 });
console.log(logs);

await sandbox.terminate();
porter.close();

Keep a sandbox alive for exec

A sandbox lives as long as its main process: the image’s default entrypoint, or the command you pass at create time. When that process exits, the sandbox moves to succeeded (exit code 0) or failed (nonzero), and it stops accepting exec calls. An image whose default command exits immediately reaches succeeded within a few seconds of starting. If you want to create a sandbox first and exec into it later, give it a long-running main process, then terminate it when the work is done:
sandbox = porter.sandboxes.create(
    image="alpine:3.20",
    name="worker",
    command=["sleep", "3600"],
)

result = sandbox.exec(["echo", "ok"])
print(result.stdout)

sandbox.terminate()
porter sandbox create alpine:3.20 --name worker -- sleep 3600
porter sandbox exec worker -- echo ok
porter sandbox terminate worker

Set environment variables

Pass environment variables at create time. The SDKs take an env map, and the CLI takes a repeatable --env KEY=VALUE flag:
sandbox = porter.sandboxes.create(
    image="alpine:3.20",
    env={"API_KEY": "secret", "LOG_LEVEL": "debug"},
)
const sandbox = await porter.sandboxes.create({
  image: "alpine:3.20",
  env: { API_KEY: "secret", LOG_LEVEL: "debug" },
});
porter sandbox create alpine:3.20 --env API_KEY=secret --env LOG_LEVEL=debug

Use custom images

Sandboxes run any container image, so you can bake in the tools your workload needs (language runtimes, CLIs, agents) instead of installing them at runtime. Private images are supported. Push your image to the ECR registry in the AWS account associated with your sandbox cluster, and sandboxes on that cluster can pull it without extra credential configuration.

Run an agent inside a sandbox

A sandbox can host an entire agent, not just execute tools for an agent that runs elsewhere. Two patterns are common:
  • Sandbox as a tool: your agent’s control loop and model calls run in your application, and the agent uses the sandbox to execute code or commands as a tool. This is the exec flow shown above.
  • Agent in the sandbox: the whole agent, its control loop and its model calls, runs inside the sandbox. Bake your harness into a custom image, inject the model API key at create time, and run it as the sandbox’s main process. Sandboxes reach the public internet by default, so an in-sandbox agent calls a hosted model API directly.
The second pattern is a custom image plus an environment variable. The agent runs as the sandbox’s main process, so the sandbox stays alive as long as the agent is running (see Sandbox lifetime):
sandbox = porter.sandboxes.create(
    image="ghcr.io/acme/agent:latest",
    name="research-agent",
    env={"MODEL_API_KEY": model_api_key},
)

# Follow the agent's output while it runs.
print(sandbox.logs(limit=100))

# Terminate when you're done, or let the agent's process exit on its own.
sandbox.terminate()
const sandbox = await porter.sandboxes.create({
  image: "ghcr.io/acme/agent:latest",
  name: "research-agent",
  env: { MODEL_API_KEY: modelApiKey },
});

// Follow the agent's output while it runs.
console.log(await sandbox.logs({ limit: 100 }));

// Terminate when you're done, or let the agent's process exit on its own.
await sandbox.terminate();
porter sandbox create ghcr.io/acme/agent:latest --name research-agent --env MODEL_API_KEY=$MODEL_API_KEY

Use tags to identify sandboxes

Tags make it easier to find sandboxes created by a workflow:
sandbox = porter.sandboxes.create(
    image="python:3.12-slim",
    tags={"workflow": "agent-run", "run": "2026-06-17"},
)

Next steps