diff --git a/openhands/usage/agent-canvas/backend-setup/docker-execution.mdx b/openhands/usage/agent-canvas/backend-setup/docker-execution.mdx index 2de327f79..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,23 +15,19 @@ 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_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 ``` @@ -39,73 +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. - -The following built-in tools execute in the container: +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. -- `terminal` -- `file_editor` -- `grep` -- `glob` -- `apply_patch` +- 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. -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. +The container is hardened as follows: - - 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. - +- 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). -## Keep the Sandbox Ephemeral +## Workspace and Persistence -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`. +Each container bind-mounts three host directories: -To mount data deliberately, provide a JSON array of Docker volume specifications: +| 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 | -```bash -export OH_EXECUTION_VOLUMES='["/path/on/host:/workspace/project"]' -``` +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. - 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 `/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. - -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. +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_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 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