From 99c7a5e47f02ca6adf4daf851b814bf700158703 Mon Sep 17 00:00:00 2001 From: Devin Date: Fri, 2 Oct 2026 12:46:04 -0400 Subject: [PATCH 1/2] fix: correct env var names in Docker execution docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace non-existent OH_EXECUTION_* env vars with the correct OH_CONVERSATION_* names that match the actual codebase: - OH_EXECUTION_RUNTIME → OH_CONVERSATION_RUNTIME - OH_EXECUTION_IMAGE → OH_CONVERSATION_IMAGE - Remove OH_EXECUTION_PLATFORM (not configurable) - Remove OH_EXECUTION_VOLUMES (not configurable via env var) - Add real container config vars: MEMORY, CPUS, PIDS_LIMIT, STARTUP_TIMEOUT Fixes #869 Co-authored-by: openhands --- .../backend-setup/docker-execution.mdx | 34 +++++++------------ 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/openhands/usage/agent-canvas/backend-setup/docker-execution.mdx b/openhands/usage/agent-canvas/backend-setup/docker-execution.mdx index 2de327f79..a3a6180df 100644 --- a/openhands/usage/agent-canvas/backend-setup/docker-execution.mdx +++ b/openhands/usage/agent-canvas/backend-setup/docker-execution.mdx @@ -18,20 +18,16 @@ This mode differs from [running the entire Agent Canvas distribution in Docker]( Set the execution runtime and image before starting Agent Canvas: ```bash -export OH_EXECUTION_RUNTIME=docker -export OH_EXECUTION_IMAGE=ghcr.io/openhands/agent-server:latest-python -export OH_EXECUTION_PLATFORM=linux/amd64 +export OH_CONVERSATION_RUNTIME=docker +export OH_CONVERSATION_IMAGE=ghcr.io/openhands/agent-server:latest-python agent-canvas ``` -Use `linux/arm64` for an ARM host such as Apple Silicon. - You can combine these variables with other launcher options. For example, to use another port: ```bash -OH_EXECUTION_RUNTIME=docker \ -OH_EXECUTION_IMAGE=ghcr.io/openhands/agent-server:latest-python \ -OH_EXECUTION_PLATFORM=linux/amd64 \ +OH_CONVERSATION_RUNTIME=docker \ +OH_CONVERSATION_IMAGE=ghcr.io/openhands/agent-server:latest-python \ agent-canvas --port 9000 ``` @@ -57,16 +53,10 @@ The outer Agent Server continues to run the agent loop and all LLM requests. It ## Keep the Sandbox Ephemeral -By default, the execution container has no host filesystem mounts. Leave `OH_EXECUTION_VOLUMES` unset to keep the workspace ephemeral and prevent host files from appearing under `/workspace`. - -To mount data deliberately, provide a JSON array of Docker volume specifications: - -```bash -export OH_EXECUTION_VOLUMES='["/path/on/host:/workspace/project"]' -``` +By default, the execution container has no host filesystem mounts. The workspace stays ephemeral and prevents host files from appearing under `/workspace`. - A volume gives tools in the container access to the mounted host path. Do not configure volumes when you require a disposable sandbox with no host filesystem access. + The execution container is designed to be disposable. Files created inside an unmounted execution container do not persist after that container is removed. Conversation history persists in the outer Agent Server according to its normal persistence configuration. The execution container: @@ -76,8 +66,6 @@ The execution container: - Is removed when its workspace closes. - Does not store the outer conversation state or LLM configuration. -Conversation history persists in the outer Agent Server according to its normal persistence configuration. Files created only inside an unmounted execution container do not persist after that container is removed. - ## Verify Isolation Create a new conversation and ask the agent to run: @@ -102,10 +90,12 @@ For an ephemeral configuration, the mounts output should be `[]`. | Variable | Default | Purpose | |----------|---------|---------| -| `OH_EXECUTION_RUNTIME` | `local` | Set to `docker` to enable one execution container per local conversation. | -| `OH_EXECUTION_IMAGE` | `ghcr.io/openhands/agent-server:latest-python` | Agent Server image used for execution containers. | -| `OH_EXECUTION_PLATFORM` | `linux/amd64` | Docker platform for execution containers. | -| `OH_EXECUTION_VOLUMES` | `[]` | Optional JSON array of Docker volume specifications. | +| `OH_CONVERSATION_RUNTIME` | `local` | Set to `docker` to enable one execution container per local conversation. | +| `OH_CONVERSATION_IMAGE` | `ghcr.io/openhands/agent-server:latest-python` | Agent Server image used for execution containers. | +| `OH_CONVERSATION_CONTAINER_MEMORY` | — | Memory limit for execution containers (e.g. `512m`). | +| `OH_CONVERSATION_CONTAINER_CPUS` | — | CPU limit for execution containers (e.g. `1.0`). | +| `OH_CONVERSATION_CONTAINER_PIDS_LIMIT` | — | PID limit for execution containers. | +| `OH_CONVERSATION_CONTAINER_STARTUP_TIMEOUT` | — | Timeout (seconds) for execution container startup. | ## Related Guides From 8e66accf0bbd1715b78ae5f1af6b4170b004e224 Mon Sep 17 00:00:00 2001 From: Devin Date: Fri, 2 Oct 2026 13:04:11 -0400 Subject: [PATCH 2/2] docs: rewrite Docker conversation runtime page to match the SDK Verified against software-agent-sdk docker_runtime/registry.py and config.py: - Each container is a full agent-server (OH_CONVERSATION_RUNTIME=local, own secret key, session API key and persistence) proxied by the outer server; drop the DockerExecutionWorkspace / execution-only endpoint / fixed tool list description. - Containers always bind-mount the conversation dir, a persistence dir and the host workspace, so files and history persist; remove the ephemeral and no-mounts claims. - Fill in real defaults (4g, 2.0 CPUs, 512 PIDs, 120s startup) and document OH_CONVERSATION_IDLE_TTL_SECONDS (20 min). - Fix the verification steps (agent-server-conversation- names, three mounts). - Update title/description and the llms.txt entries. Co-Authored-By: Claude Sonnet 5.5 --- llms-full.txt | 2 +- llms.txt | 2 +- .../backend-setup/docker-execution.mdx | 78 ++++++++++--------- 3 files changed, 44 insertions(+), 38 deletions(-) diff --git a/llms-full.txt b/llms-full.txt index e5697256d..62427b260 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -29886,7 +29886,7 @@ Then add the Docker backend: - [Kubernetes (Helm)](/openhands/usage/agent-canvas/backend-setup/kubernetes) - [Running Docker in the Agent Sandbox](/enterprise/docker-in-sandbox) — how Enterprise does this without `--privileged` -### Isolate Tool Execution with Docker +### Run Each Conversation in Its Own Docker Container Source: https://docs.openhands.dev/openhands/usage/agent-canvas/backend-setup/docker-execution.md Use Docker execution mode when you want Agent Canvas and Agent Server to remain trusted host processes while isolating filesystem and process tools in a separate container for each conversation. diff --git a/llms.txt b/llms.txt index 638b3c95d..7dea04003 100644 --- a/llms.txt +++ b/llms.txt @@ -168,7 +168,7 @@ from the OpenHands Software Agent SDK. - [Incident Triage](https://docs.openhands.dev/openhands/usage/use-cases/incident-triage.md): Using OpenHands to investigate and resolve production incidents - [Install Agent Canvas](https://docs.openhands.dev/openhands/usage/agent-canvas/setup.md): Install, run, update, or uninstall Agent Canvas. - [Integrations Settings](https://docs.openhands.dev/openhands/usage/settings/integrations-settings.md): How to setup and modify the various integrations in OpenHands. -- [Isolate Tool Execution with Docker](https://docs.openhands.dev/openhands/usage/agent-canvas/backend-setup/docker-execution.md): Keep Agent Canvas orchestration on the host while running filesystem and process tools in an ephemeral Docker container per conversation. +- [Run Each Conversation in Its Own Docker Container](https://docs.openhands.dev/openhands/usage/agent-canvas/backend-setup/docker-execution.md): Keep Agent Canvas on the host while each conversation runs in its own hardened Agent Server container with bind-mounted workspace and persistence directories. - [Key Features](https://docs.openhands.dev/openhands/usage/key-features.md) - [Kubernetes (Helm)](https://docs.openhands.dev/openhands/usage/agent-canvas/backend-setup/kubernetes.md): Install Agent Canvas into a Kubernetes cluster with the official Helm chart. - [Language Model (LLM) Settings](https://docs.openhands.dev/openhands/usage/settings/llm-settings.md): This page goes over how to set the LLM to use in OpenHands, including LLM profiles for switching models during conversations. diff --git a/openhands/usage/agent-canvas/backend-setup/docker-execution.mdx b/openhands/usage/agent-canvas/backend-setup/docker-execution.mdx index a3a6180df..032b651a5 100644 --- a/openhands/usage/agent-canvas/backend-setup/docker-execution.mdx +++ b/openhands/usage/agent-canvas/backend-setup/docker-execution.mdx @@ -1,11 +1,11 @@ --- -title: Isolate Tool Execution with Docker -description: Keep Agent Canvas orchestration on the host while running filesystem and process tools in an ephemeral Docker container per conversation. +title: Run Each Conversation in Its Own Docker Container +description: Keep Agent Canvas on the host while each conversation runs in its own hardened Agent Server container with bind-mounted workspace and persistence directories. --- -Use Docker execution mode when you want Agent Canvas and Agent Server to remain trusted host processes while isolating filesystem and process tools in a separate container for each conversation. +Use per-conversation Docker mode when you want Agent Canvas and the outer Agent Server to remain trusted host processes while each conversation runs in a separate, hardened Docker container. -This mode differs from [running the entire Agent Canvas distribution in Docker](/openhands/usage/agent-canvas/backend-setup/docker). The outer Agent Server retains conversation state, LLM calls, credentials, policy, persistence, and orchestration. Supported tool actions run inside an ephemeral execution container. +This mode differs from [running the entire Agent Canvas distribution in Docker](/openhands/usage/agent-canvas/backend-setup/docker). Here, only conversations run in containers; Agent Canvas and the outer Agent Server stay on the host. ## Prerequisites @@ -15,7 +15,7 @@ This mode differs from [running the entire Agent Canvas distribution in Docker]( ## Start Agent Canvas -Set the execution runtime and image before starting Agent Canvas: +Set the conversation runtime and image before starting Agent Canvas: ```bash export OH_CONVERSATION_RUNTIME=docker @@ -35,67 +35,73 @@ The launcher forwards the variables to the local Agent Server. No separate front ## How Isolation Works -For each local conversation, Agent Server creates a `DockerExecutionWorkspace` with `/workspace` as its working directory. The container starts lazily when the conversation first invokes a supported tool. +For each conversation, the outer Agent Server starts a dedicated Docker container running a full Agent Server. The container starts lazily when the conversation first needs it. -The following built-in tools execute in the container: +- The inner server runs with `OH_CONVERSATION_RUNTIME=local`, so it runs the agent loop and its own tools inside the container. +- Each container has its own generated `OH_SECRET_KEY`, its own session API key, and its own persistence. +- The outer Agent Server proxies and mediates conversation requests to the container, and resolves agent and profile settings before forwarding them. -- `terminal` -- `file_editor` -- `grep` -- `glob` -- `apply_patch` +The container is hardened as follows: -The outer Agent Server continues to run the agent loop and all LLM requests. It sends supported tool actions to an authenticated execution-only endpoint in the container. The inner server does not expose conversation, profile, settings, LLM, persistence, or WebSocket APIs. +- Runs as the host user's UID and GID rather than root. +- Drops all Linux capabilities (`--cap-drop ALL`) and sets `no-new-privileges`. +- Publishes its API only on `127.0.0.1`, authenticated with the per-conversation session API key. +- Maps `host.docker.internal` to the host gateway. +- Is limited by the memory, CPU, and PID settings in the [configuration reference](#configuration-reference). - - Tools without a Docker execution adapter continue to run in the outer Agent Server process. Review custom and additional tools before treating the container as their security boundary. - +## Workspace and Persistence -## Keep the Sandbox Ephemeral +Each container bind-mounts three host directories: -By default, the execution container has no host filesystem mounts. The workspace stays ephemeral and prevents host files from appearing under `/workspace`. +| Container path | Host directory | +|----------------|----------------| +| `/var/openhands/conversations/` | The conversation's directory under the outer server's conversations path | +| `/var/openhands/.openhands` | A per-conversation persistence directory under the outer server's runtime data root | +| `/workspace` | The conversation's host workspace | + +Because these are bind mounts, workspace files and conversation history persist after the container is removed. Conversations that share a host workspace share the same files. - The execution container is designed to be disposable. Files created inside an unmounted execution container do not persist after that container is removed. Conversation history persists in the outer Agent Server according to its normal persistence configuration. + The `/workspace` mount gives tools in the container read and write access to that host directory. Point conversations at a dedicated workspace if you do not want the agent to modify existing project files. -The execution container: - -- Publishes its API only on host loopback. -- Receives a generated per-workspace capability instead of the outer server's credentials. -- Is removed when its workspace closes. -- Does not store the outer conversation state or LLM configuration. +Containers are stopped when a conversation has been idle for the idle timeout (`OH_CONVERSATION_IDLE_TTL_SECONDS`, 20 minutes by default). The conversation is not lost; its container is started again on next use. Stale containers from a previous Agent Server run are removed when the server starts. ## Verify Isolation Create a new conversation and ask the agent to run: ```bash +id printf 'PWD=%s\nHOME=%s\n' "$PWD" "$HOME" -find "$HOME" -mindepth 1 -maxdepth 1 -printf '%f\n' | sort ``` -A default execution image should report `/workspace` as `PWD` and a container-local home directory such as `/home/openhands`. It must not display the Agent Server host's home-directory contents. +The command should report `/workspace` as `PWD`, and a `HOME` of `/var/openhands/.openhands` rather than your host home directory. -On the host, inspect the active execution container: +On the host, inspect the active conversation container: ```bash -docker ps --filter name=openhands-execution- +docker ps --filter name=agent-server-conversation- docker inspect --format '{{json .Mounts}}' ``` -For an ephemeral configuration, the mounts output should be `[]`. +The mounts output should list three bind mounts, with destinations `/var/openhands/conversations/`, `/var/openhands/.openhands`, and `/workspace`. To check the other settings: + +```bash +docker inspect --format '{{.HostConfig.CapDrop}} {{.HostConfig.SecurityOpt}} {{.HostConfig.PortBindings}}' +``` ## Configuration Reference | Variable | Default | Purpose | |----------|---------|---------| -| `OH_CONVERSATION_RUNTIME` | `local` | Set to `docker` to enable one execution container per local conversation. | -| `OH_CONVERSATION_IMAGE` | `ghcr.io/openhands/agent-server:latest-python` | Agent Server image used for execution containers. | -| `OH_CONVERSATION_CONTAINER_MEMORY` | — | Memory limit for execution containers (e.g. `512m`). | -| `OH_CONVERSATION_CONTAINER_CPUS` | — | CPU limit for execution containers (e.g. `1.0`). | -| `OH_CONVERSATION_CONTAINER_PIDS_LIMIT` | — | PID limit for execution containers. | -| `OH_CONVERSATION_CONTAINER_STARTUP_TIMEOUT` | — | Timeout (seconds) for execution container startup. | +| `OH_CONVERSATION_RUNTIME` | `local` | Set to `docker` to run each conversation in its own container. | +| `OH_CONVERSATION_IMAGE` | `ghcr.io/openhands/agent-server:latest-python` | Agent Server image used for conversation containers. | +| `OH_CONVERSATION_CONTAINER_MEMORY` | `4g` | Memory limit for each container. | +| `OH_CONVERSATION_CONTAINER_CPUS` | `2.0` | CPU limit for each container. | +| `OH_CONVERSATION_CONTAINER_PIDS_LIMIT` | `512` | PID limit for each container. | +| `OH_CONVERSATION_CONTAINER_STARTUP_TIMEOUT` | `120` | Seconds to wait for a container to become healthy. | +| `OH_CONVERSATION_IDLE_TTL_SECONDS` | `1200` | Seconds a conversation can be idle before its container is stopped. | ## Related Guides