> ## Documentation Index
> Fetch the complete documentation index at: https://praison.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Shared Sandbox

> Give a whole team or workflow ONE remote sandbox with a shared /workspace

Set `run_on=` (Python) or `run_on:` (YAML) once on a flow, team, or workflow and every agent shares one remote sandbox, so a file written by one step is visible to the next.

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent, AgentFlow

writer = Agent(name="writer", instructions="Write 'SECRET-42' to /workspace/data.txt")
reader = Agent(name="reader", instructions="Read /workspace/data.txt and print the contents")

flow = AgentFlow(run_on="docker", steps=[writer, reader])
flow.run("Step 1: write. Step 2: read it back.")
```

> [!TIP]
>
> Confirm the topology of a flow before you run it — `print(repr(flow))` names both places. See [Where Your Agent Runs](https://praison.ai/docs/features/where-does-it-run.md).

> [!NOTE]
>
> Files an agent writes inside a remote sandbox reach the user through the outbound media path — see [Remote-sandbox media](https://praison.ai/docs/features/outbound-media-delivery.md#remote-sandbox-media-modal-e2b-daytona-fly-io-ssh).

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    User[👤 User] --> Flow[AgentFlow / AgentTeam<br/>run_on=docker]
    Flow --> SANDBOX
    subgraph SANDBOX["🐳 One Shared /workspace"]
        W[✍️ Writer] --> F[(📄 data.txt)]
        F --> R[👀 Reader]
    end
    SANDBOX --> Out[✅ Result]

    classDef input fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef process fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef tools fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef result fill:#10B981,stroke:#7C90A0,color:#fff

    class User,W,R input
    class Flow process
    class F tools
    class Out result
```

Without a shared sandbox, each agent provisions its own isolated instance, so agents cannot hand files to each other. `run_on=` gives them all the same `/workspace`.

## Quick Start

**1. Share one sandbox across a workflow**

Set `run_on` once — every step runs its shell and file tools in one sandbox. Python uses `run_on=`; no-code YAML uses the same `run_on:` key.

> [tab] Python
>
> ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
> from praisonaiagents import Agent, AgentFlow
>
> writer = Agent(name="writer", instructions="Write 'SECRET-42' to /workspace/data.txt")
> reader = Agent(name="reader", instructions="Read /workspace/data.txt and print the contents")
>
> flow = AgentFlow(run_on="docker", steps=[writer, reader])
> flow.run("Step 1: write. Step 2: read it back.")
> ```

> [tab] YAML
>
> ```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
> name: remote-sandbox-demo
> run_on: docker            # every step shares one sandbox
>
> agents:
>   writer:
>     role: File Writer
>     goal: Write files into the shared workspace
>   reader:
>     role: File Reader
>     goal: Read files back from the shared workspace
>
> steps:
>   - agent: writer
>     action: "Write 'hello from step 1' to /workspace/note.txt"
>   - agent: reader
>     action: "Read /workspace/note.txt and report its exact contents"
> ```
>
> Run it:
>
> ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
> praisonai examples/yaml/workflows/remote_sandbox_workflow.yaml
> ```

**2. Share one sandbox across a team**

The same `run_on=` kwarg works on a team.

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent, Task, AgentTeam

writer = Agent(name="writer", instructions="Write 'SECRET-42' to /workspace/data.txt")
reader = Agent(name="reader", instructions="Read /workspace/data.txt and print the contents")

write_task = Task(description="Write 'SECRET-42' to /workspace/data.txt", agent=writer)
read_task = Task(description="Read /workspace/data.txt and report it", agent=reader)

team = AgentTeam(
    agents=[writer, reader],
    tasks=[write_task, read_task],
    run_on="docker",
)
team.start()
```

**3. From workflow YAML — no Python**

Add one line — `run_on: docker` — to any workflow YAML and every step shares the same sandbox. The rest of the file is a normal workflow.

```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
name: remote-sandbox-demo
run_on: docker            # every step shares ONE sandbox

agents:
  writer:
    role: File Writer
    goal: Write files into the shared workspace
    instructions: >
      You have write_file and execute_command running on a remote Linux
      sandbox. Use them; never say you cannot run commands.

  reader:
    role: File Reader
    goal: Read files back out of the shared workspace
    instructions: >
      You have read_file and execute_command running on the same remote
      sandbox the writer used. Use them.

steps:
  - agent: writer
    action: "Use write_file to put exactly 'hello from step 1' in /workspace/note.txt"

  - agent: reader
    action: "Use read_file on /workspace/note.txt and report its exact contents"
```

Run it with the standard CLI — no Python needed:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai examples/yaml/workflows/remote_sandbox_workflow.yaml
```

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant User
    participant CLI as praisonai CLI
    participant Sandbox as Shared /workspace
    participant Writer
    participant Reader

    User->>CLI: praisonai remote_sandbox_workflow.yaml
    CLI->>Writer: step 1 (writer)
    Writer->>Sandbox: write_file("/workspace/note.txt")
    Note over Sandbox: provision ONE sandbox on first use
    Sandbox-->>Writer: wrote note.txt
    CLI->>Reader: step 2 (reader)
    Reader->>Sandbox: read_file("/workspace/note.txt")
    Sandbox-->>Reader: "hello from step 1"
    CLI->>Sandbox: run ends → tear down
```

The accepted values are identical to the Python API — `docker | e2b | modal | daytona | flyio | tenki | local`, or omit `run_on:` entirely for the default local behaviour (see the [Configuration Options](#configuration-options) table below). Action-only steps like `- agent: X; action: "..."` share the same sandbox as regular steps, so the writer/reader pattern above works exactly as shown.

> [!TIP]
>
> A typo fails **when the file loads**, before any container starts, with the list of valid names — you are never surprised mid-run:
>
> ```
> ValueError: Unknown run_on provider 'dokcer'. Available: docker, e2b, modal, daytona, flyio, tenki, local
> ```
>
> The value is case- and whitespace-insensitive — `RUN_ON: DOCKER` and `run_on: "  docker  "` both normalise to `docker`. Omitting `run_on:` leaves every existing workflow YAML behaving exactly as before — nothing is provisioned.

**4. Pick a provider**

Pass any provider name, or a pre-configured provider instance to customise the image and resources. The provider name is identical for Python and YAML.

> [tab] Python
>
> > [tab] docker
> >
> > ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
> > flow = AgentFlow(run_on="docker", steps=[writer, reader])
> > ```
>
> > [tab] e2b
> >
> > ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
> > flow = AgentFlow(run_on="e2b", steps=[writer, reader])
> > ```
>
> > [tab] modal
> >
> > ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
> > flow = AgentFlow(run_on="modal", steps=[writer, reader])
> > ```
>
> > [tab] daytona
> >
> > ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
> > flow = AgentFlow(run_on="daytona", steps=[writer, reader])
> > ```
>
> > [tab] flyio
> >
> > ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
> > flow = AgentFlow(run_on="flyio", steps=[writer, reader])
> > ```
>
> > [tab] tenki
> >
> > ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
> > flow = AgentFlow(run_on="tenki", steps=[writer, reader])
> > ```
>
> > [tab] local
> >
> > ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
> > flow = AgentFlow(run_on="local", steps=[writer, reader])
> > ```

> [tab] YAML
>
> > [tab] docker
> >
> > ```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
> > run_on: docker
> > ```
>
> > [tab] e2b
> >
> > ```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
> > run_on: e2b
> > ```
>
> > [tab] modal
> >
> > ```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
> > run_on: modal
> > ```
>
> > [tab] daytona
> >
> > ```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
> > run_on: daytona
> > ```
>
> > [tab] flyio
> >
> > ```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
> > run_on: flyio
> > ```
>
> > [tab] tenki
> >
> > ```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
> > run_on: tenki
> > ```
>
> > [tab] local
> >
> > ```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
> > run_on: local
> > ```

***

## How It Works

One sandbox is provisioned lazily on first tool use and torn down when the run ends, even if a step raises.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant User
    participant Flow as AgentFlow
    participant Sandbox as Shared /workspace
    participant Writer
    participant Reader

    User->>Flow: flow.run("write then read")
    Note over Flow,Sandbox: nothing provisioned yet
    Flow->>Writer: run step 1
    Writer->>Sandbox: write_file("/workspace/data.txt")
    Note over Sandbox: provision on first use
    Sandbox-->>Writer: Wrote /workspace/data.txt
    Flow->>Reader: run step 2
    Reader->>Sandbox: read_file("/workspace/data.txt")
    Sandbox-->>Reader: "SECRET-42"
    Flow->>Sandbox: run ends → tear down
```

| Behaviour | What actually happens |
| :- | :- |
| One sandbox per run | `run_on=` provisions a single sandbox for the whole run and binds every agent's shell and file tools to it. |
| Shared `/workspace` | Files a writer step drops there are visible to a later reader step — they run in the same container. |
| Orchestration stays local | Routing, parallel, and repeat run on this machine; only shell and file tools follow the sandbox. |
| Opt-in and lazy | No `run_on=` means nothing changes, and nothing is provisioned until a tool actually runs. |
| Guaranteed teardown | The sandbox is released on exit even if a step raises. |
| Backend-owning agents skipped | Agents that already carry their own `backend=` are left alone — they stay pointed at their own runtime. |

> [!TIP]
>
> **Action-only steps join the sandbox too.** A step with `action:` but no matching `agent:` builds a temporary agent during the run — that temp agent shares the same sandbox, so no shell or file tool escapes to the host.

***

## Load-time Validation

A typo in `run_on:` fails **when the YAML loads**, not halfway through a paid run.

The provider name is checked against the registry at parse time. An unknown provider raises with the full list of valid names:

```
ValueError: Unknown run_on provider 'dokcer'.
Available: daytona, docker, e2b, flyio, local, modal, tenki
```

A non-string value raises immediately too:

```
ValueError: 'run_on' must be a provider name string, got int
```

The value is case- and whitespace-insensitive — `DOCKER` and `"  docker  "` both normalise to `docker`.

***

## Configuration Options

`run_on=` accepts a provider name or a pre-configured provider instance. The accepted values are identical for Python (`run_on="docker"`) and YAML (`run_on: docker`).

| Value | Meaning |
| :- | :- |
| `None` (default) | No shared sandbox. Existing per-agent behaviour is unchanged. |
| `"local"` | Run inside a local subprocess (no isolation). Useful for testing without cloud. |
| `"docker"` | Local Docker container. Requires Docker running. |
| `"e2b"` | E2B cloud sandbox. Requires `E2B_API_KEY`. |
| `"modal"` | Modal cloud compute. Requires `MODAL_TOKEN`. |
| `"daytona"` | Daytona workspace. |
| `"flyio"` | Fly.io machine. |
| `"tenki"` | Tenki Cloud microVM. Requires `TENKI_API_KEY`. |
| Provider instance | Any object implementing `ComputeProviderProtocol`. Pass this to customise image, packages, CPU, or memory. |

### Which param goes where

The shared-sandbox knob is `run_on=` on the flow, the team, or the top-level YAML key. Individual agents that need their own remote sandbox use `compute=`.

| Parameter | Level | Effect |
| :- | :- | :- |
| `AgentFlow(run_on=...)` / `AgentTeam(run_on=...)` | Team / workflow | ONE shared sandbox for every agent in the run. |
| `run_on:` (YAML top-level key) | Workflow | Same key — one shared sandbox for every step in `agents.yaml` / `workflow.yaml`. |
| `LocalAgent(compute=...)` | Per-agent | That single agent runs its tools on the named provider. |

> [!NOTE]
>
> `run_on=` shares one sandbox across a whole flow or team. A single `LocalAgent` uses `compute=` for its own remote provider. See [Placement](https://praison.ai/docs/features/placement.md) for how these fit together.

> [!NOTE]
>
> As of [PR #4071](https://github.com/MervinPraison/PraisonAI/pull/4071), `run_on=` accepts the full compute set — `anthropic`, `docker`, `e2b`, `modal`, `daytona`, `flyio`, `tenki`, `novita` — the same places as `tools_run_on=` for compute-backed backends. See [Placement](https://praison.ai/docs/features/placement.md#where-run-on-and-compute-can-point).

> [!NOTE]
>
> As of [PR #4092](https://github.com/MervinPraison/PraisonAI/pull/4092), the vendor compute providers live in the standalone `praisonai-sandbox` package (same names, same behaviour; old `praisonai.integrations.compute.*` imports still work through a shim).

***

## Common Patterns

Hand a file between two agents — step 1 writes, step 2 reads the same `/workspace`.

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent, AgentFlow

writer = Agent(name="writer", instructions="Write files to /workspace")
reader = Agent(name="reader", instructions="Read files from /workspace")

flow = AgentFlow(run_on="docker", steps=[writer, reader])
flow.run("Step 1: write /workspace/data.txt. Step 2: read it back.")
```

Share one sandbox across a team where one agent's output file is another agent's input.

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent, Task, AgentTeam

writer = Agent(name="writer", instructions="Write files to /workspace")
reader = Agent(name="reader", instructions="Read files from /workspace")

team = AgentTeam(
    agents=[writer, reader],
    tasks=[
        Task(description="Write /workspace/report.md", agent=writer),
        Task(description="Read /workspace/report.md and summarise it", agent=reader),
    ],
    run_on="docker",
)
team.start()
```

***

## Choosing a Provider

Pick the provider that matches where you want the sandbox to live.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph TB
    Start[Where should the sandbox run?] --> Local{Local dev}
    Start --> Cloud{Cloud}
    Start --> Test{Testing}

    Local --> Docker["docker — local container"]
    Cloud --> Quick["e2b — quick cloud sandbox"]
    Cloud --> Scale["modal — scale out"]
    Cloud --> Persist["daytona — persistent workspace"]
    Cloud --> Micro["tenki — disposable microVM"]
    Test --> LocalRun["local — no cloud needed"]

    classDef question fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef option fill:#10B981,stroke:#7C90A0,color:#fff

    class Start,Local,Cloud,Test question
    class Docker,Quick,Scale,Persist,Micro,LocalRun option
```

| Scenario | Provider |
| :- | :- |
| Local development | `"docker"` |
| Quick cloud sandbox | `"e2b"` |
| Scale out | `"modal"` |
| Persistent workspace | `"daytona"` |
| Disposable microVM | `"tenki"` |
| Testing without cloud | `"local"` |

***

## Best Practices

> [accordion] Use run\_on= when agents share files
>
> Per-agent `compute=` gives each agent its own isolated instance, so files written by one agent are invisible to the next. Set `run_on=` on the flow or team when agents need the same `/workspace`.

> [accordion] Leave run\_on unset unless you need shared execution
>
> The default is zero-overhead — nothing is provisioned. Only set `run_on=` when a run actually shares files or shell state across agents.

> [accordion] Size the sandbox for the heaviest step
>
> All steps share one instance. Pass a configured provider instance to size the image, CPU, and memory for the biggest step, since it affects the whole run.

> [accordion] Don't mix run\_on= with agents that carry their own backend=
>
> Agents that already carry their own `backend=` are deliberately skipped — they keep pointing at their own runtime. Use one approach or the other per agent.

***

## Related

> [card] [Where Does It Run](https://praison.ai/docs/features/where-does-it-run.md)
>
> Ask a flow where its thinking and tools actually run — the "shared" note lives here.

> [card] [YAML Workflows](https://praison.ai/docs/features/yaml-workflows.md)
>
> Full no-code workflow reference — including the top-level `run_on:` key.

> [card] [Managed Agents](https://praison.ai/docs/concepts/managed-agents.md)
>
> Per-agent managed execution and providers.

> [card] [Managed CLI](https://praison.ai/docs/features/managed-cli.md)
>
> Use `managed ps` / `stop` to find and reclaim stray sandboxes.

> [card] [Managed Agents (Docker)](https://praison.ai/docs/concepts/managed-agents-docker.md)
>
> Docker daemon setup and provider details.

> [card] [Managed Agents (E2B)](https://praison.ai/docs/concepts/managed-agents-e2b.md)
>
> E2B cloud sandbox and `E2B_API_KEY` setup.

> [card] [Managed Agents (Modal)](https://praison.ai/docs/concepts/managed-agents-modal.md)
>
> Modal cloud compute setup.

> [card] [Tenki Cloud](https://praison.ai/docs/features/managed-agents-tenki.md)
>
> Tenki microVM and `TENKI_API_KEY` setup.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.