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

# OpenHands

> Run OpenHands Agent Canvas in a secure E2B sandbox and open its web UI from your browser.

[OpenHands](https://github.com/OpenHands/OpenHands) is an open-source coding agent. Its control center, Agent Canvas, bundles a web UI, the OpenHands Agent Server that runs conversations and tools, and an automation backend behind one port. E2B provides a pre-built `openhands` template with Agent Canvas, its Python services, and Chromium already installed, so you can run the agent in an E2B sandbox without installing it yourself.

## Start Agent Canvas

The script below creates a sandbox, starts Agent Canvas in public mode, and waits for its `/health` endpoint on port 8000 before it prints the URL. Generate the Agent Canvas API key yourself and pass it in `AGENT_CANVAS_API_KEY`, for example with `export AGENT_CANVAS_API_KEY=$(openssl rand -base64 32)`. Don't print or log the key. Public mode makes the UI ask for the key, so only people you share it with can use the agent. Use a different key for every sandbox.

```typescript JavaScript & TypeScript theme={"theme":"css-variables"}
import { Sandbox } from 'e2b'

// Your own secret, e.g. `export AGENT_CANVAS_API_KEY=$(openssl rand -base64 32)`
const apiKey = process.env.AGENT_CANVAS_API_KEY!

const sandbox = await Sandbox.create('openhands', {
  timeoutMs: 60 * 60 * 1000,
  lifecycle: { onTimeout: 'pause', autoResume: true },
})

const canvas = await sandbox.commands.run(
  'agent-canvas --public > /tmp/agent-canvas.log 2>&1',
  {
    background: true,
    timeoutMs: 0,
    envs: { LOCAL_BACKEND_API_KEY: apiKey },
  }
)
// Disconnect from the command: an attached command stream wakes the sandbox right after it pauses
await canvas.disconnect()

// Agent Canvas is usually ready in about 30 seconds
const url = `https://${sandbox.getHost(8000)}`
while (!(await fetch(`${url}/health`).then((r) => r.ok, () => false))) {
  await new Promise((resolve) => setTimeout(resolve, 2000))
}

console.log(`Agent Canvas: ${url}`)
```

```python Python theme={"theme":"css-variables"}
import os
import time
import urllib.request
from e2b import Sandbox

# Your own secret, e.g. `export AGENT_CANVAS_API_KEY=$(openssl rand -base64 32)`
api_key = os.environ["AGENT_CANVAS_API_KEY"]

sandbox = Sandbox.create(
    "openhands",
    timeout=60 * 60,
    lifecycle={"on_timeout": "pause", "auto_resume": True},
)

canvas = sandbox.commands.run(
    "agent-canvas --public > /tmp/agent-canvas.log 2>&1",
    background=True,
    timeout=0,
    envs={"LOCAL_BACKEND_API_KEY": api_key},
)
# Disconnect from the command: an attached command stream wakes the sandbox right after it pauses
canvas.disconnect()

# Agent Canvas is usually ready in about 30 seconds
url = f"https://{sandbox.get_host(8000)}"
while True:
    try:
        urllib.request.urlopen(f"{url}/health", timeout=5)
        break
    except OSError:
        time.sleep(2)

print(f"Agent Canvas: {url}")
```

Open the printed URL and enter your Agent Canvas API key. Then add your model provider and its API key in the Agent Canvas settings. Agent Canvas stores its state, including conversations, settings, and workspaces, under `/home/user/.openhands` in the sandbox. Its logs are in `/tmp/agent-canvas.log`.

> [!NOTE]
>
> The agent can run shell commands, edit files, and browse the web inside the sandbox. E2B isolates those actions from your machine, so the agent cannot reach your host files or credentials unless you pass them in. Sandboxes can reach the internet by default — restrict outbound traffic with [network rules](https://docs.e2b.dev/network/internet-access.md) when the agent handles untrusted input or secrets. Hostname rules apply to HTTP(S) traffic only; use CIDR rules for other protocols.

## Timeouts and pausing

By default, the sandbox is killed when its timeout expires, and your conversations and workspace are lost with it. The example above sets `onTimeout: 'pause'` so the sandbox pauses instead, and `autoResume: true` so it wakes up on the next request to its URL. See [Auto-resume](https://docs.e2b.dev/sandbox/auto-resume.md) for details.

The agent's work does not extend the timeout, and neither do requests to the sandbox URL, including those from the Agent Canvas UI. When the timeout expires, the sandbox pauses with its memory and processes intact:

* With Agent Canvas open in a browser tab, the UI resumes the sandbox within seconds and the conversation continues.
* With nobody connected, the agent waits while the sandbox is paused and continues when you open the URL again. Scheduled automations don't run while the sandbox is paused.

After it resumes, the sandbox runs for the original timeout again, or at least 5 minutes.

To keep the agent working without pauses, extend the timeout while you use the sandbox.

```typescript JavaScript & TypeScript theme={"theme":"css-variables"}
// Extend the timeout to 15 minutes every 5 minutes while this process runs
setInterval(() => sandbox.setTimeout(15 * 60 * 1000).catch(console.error), 5 * 60 * 1000)
```

```python Python theme={"theme":"css-variables"}
# Extend the timeout to 15 minutes every 5 minutes while this process runs
while True:
    time.sleep(5 * 60)
    sandbox.set_timeout(15 * 60)
```

On the Hobby plan, a sandbox can run continuously for up to 1 hour. On the Pro plan, the limit is 24 hours. Extending the timeout doesn't lift this limit, but pausing and resuming resets it. Paused sandboxes are kept until you kill them. See [Sandbox lifetime](https://docs.e2b.dev/faq/sandbox-lifetime.md).

When you no longer need the agent, kill the sandbox with `sandbox.kill()`.

## Use the sandbox as a backend for your local Agent Canvas

Agent Canvas can switch between several backends. To keep the UI on your machine and run the agent in E2B, start only the backend in the sandbox. Start it the same way as in the example above, with `--backend-only` added and `LOCAL_BACKEND_API_KEY` set to your key:

```bash theme={"theme":"css-variables"}
agent-canvas --backend-only --public
```

Then run the UI on your machine:

```bash theme={"theme":"css-variables"}
npx @openhands/agent-canvas --frontend-only
```

In Agent Canvas, open **Manage Backends**, add a backend, and enter the sandbox URL and the Agent Canvas API key. Settings, model configuration, and conversations belong to the backend, so each sandbox keeps its own. See [Backends](https://docs.openhands.dev/openhands/usage/agent-canvas/backends) in the OpenHands documentation.

## Build a custom template

If you need to customize the environment (e.g. pre-install dependencies, add config files), build your own template on top of the pre-built `openhands` template.

```typescript JavaScript & TypeScript theme={"theme":"css-variables"}
// template.ts
import { Template } from 'e2b'

export const template = Template()
  .fromTemplate('openhands')
```

```python Python theme={"theme":"css-variables"}
# template.py
from e2b import Template

template = (
    Template()
    .from_template("openhands")
)
```

```typescript JavaScript & TypeScript theme={"theme":"css-variables"}
// build.ts
import { Template, defaultBuildLogger } from 'e2b'
import { template as openhandsTemplate } from './template'

await Template.build(openhandsTemplate, 'my-openhands', {
  cpuCount: 2,
  memoryMB: 4096,
  onBuildLogs: defaultBuildLogger(),
})
```

```python Python theme={"theme":"css-variables"}
# build.py
from e2b import Template, default_build_logger
from template import template as openhands_template

Template.build(openhands_template, "my-openhands",
    cpu_count=2,
    memory_mb=4096,
    on_build_logs=default_build_logger(),
)
```

Run the build script to create the template.

```bash JavaScript & TypeScript theme={"theme":"css-variables"}
npx tsx build.ts
```

```bash Python theme={"theme":"css-variables"}
python build.py
```

Then create sandboxes from `my-openhands` instead of `openhands`, and start Agent Canvas as shown above.

## Related guides

> [card] [Cookbook example](https://github.com/e2b-dev/e2b-cookbook/tree/main/examples/openhands-in-sandbox-python)
>
> Fix a failing test in a small Python project from code and print the diff

> [card] [Auto-resume](https://docs.e2b.dev/sandbox/auto-resume.md)
>
> Pause on timeout and wake up on the next request

> [card] [Sandbox persistence](https://docs.e2b.dev/sandbox/persistence.md)
>
> Auto-pause, resume, and manage sandbox lifecycle

> [card] [Internet access](https://docs.e2b.dev/network/internet-access.md)
>
> Control the sandbox's outbound network access
