From fab90211864c4fb548f22651d53c63af9ad4038e Mon Sep 17 00:00:00 2001 From: Robert Brennan Date: Tue, 2 Jun 2026 12:22:33 -0400 Subject: [PATCH 1/4] docs: add custom Agent Server image guide for Docker backend Replace the placeholder Docker backend page with a full walkthrough: build a custom Agent Server image (FROM ghcr.io/openhands/agent-server, add JDK + utilities), run it locally with a SESSION_API_KEY, and register it as a backend in Agent Canvas. Co-authored-by: openhands --- .../agent-canvas/backend-setup/docker.mdx | 122 +++++++++++++++++- 1 file changed, 120 insertions(+), 2 deletions(-) diff --git a/openhands/usage/agent-canvas/backend-setup/docker.mdx b/openhands/usage/agent-canvas/backend-setup/docker.mdx index 201241d0..e35da608 100644 --- a/openhands/usage/agent-canvas/backend-setup/docker.mdx +++ b/openhands/usage/agent-canvas/backend-setup/docker.mdx @@ -1,6 +1,124 @@ --- title: Setup a Docker Backend -description: Run an OpenHands agent server in Docker and connect Agent Canvas to it. +description: Build a custom OpenHands Agent Server image and connect Agent Canvas to it. --- -Coming soon. +This guide walks through building a **custom Agent Server image** that layers extra tooling (for example, the JDK) on top of the official base image, running it locally with Docker, and registering it as a backend in Agent Canvas. + +Use this approach when the default backend's toolchain is missing something your agent needs, such as a specific language runtime, build tool, or CLI. + +## Prerequisites + +- Docker installed and running +- An LLM API key (Anthropic, OpenAI, etc.) +- Agent Canvas installed and running locally — see [Setup](/openhands/usage/agent-canvas/setup) + +## 1. Build a Custom Image + +The OpenHands Agent Server publishes a base image at `ghcr.io/openhands/agent-server`. Extend it with a small `Dockerfile` that installs whatever your agent needs. + +The example below adds the JDK and a few common utilities. Save it as `Dockerfile`: + +```dockerfile icon="docker" +FROM ghcr.io/openhands/agent-server:latest-python + +USER root + +# Install Java (JDK) and common build/diagnostic utilities. +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + default-jdk \ + maven \ + unzip \ + vim \ + less \ + && rm -rf /var/lib/apt/lists/* + +# Switch back to the unprivileged user that the base image runs as. +USER openhands +``` + + + The base image already ships with Python, Node.js, `git`, `jq`, `tmux`, build tools, and a VS Code Web + VNC stack. Only add what you actually need on top. + + +Build the image with a tag you can reference later: + +```bash +docker build -t my-agent-server:latest . +``` + + + For ARM hosts (Apple Silicon, Graviton), prefer the matching base tag (for example `latest-python` already supports multi-arch). If you need to force a platform, pass `--platform linux/amd64` to `docker build`. + + +## 2. Run the Custom Image + +Start the image, expose the agent server's HTTP port, and protect it with a session API key. The agent server reads `SESSION_API_KEY` from the environment and requires the same value on the `X-Session-API-Key` header for every request. + +```bash +# Pick any sufficiently random value; you'll paste it into Agent Canvas later. +export SESSION_API_KEY="$(openssl rand -hex 32)" + +docker run -d \ + --name openhands-agent-server \ + -p 8000:8000 \ + -e SESSION_API_KEY="$SESSION_API_KEY" \ + my-agent-server:latest +``` + +Verify the server is reachable: + +```bash +curl -H "X-Session-API-Key: $SESSION_API_KEY" http://localhost:8000/health +``` + + + The agent server can execute arbitrary shell commands inside the container. Treat `SESSION_API_KEY` like a production credential and **do not expose port 8000 to the public internet** without a TLS-terminating reverse proxy in front of it. + + +To follow logs or stop the container: + +```bash +docker logs -f openhands-agent-server +docker stop openhands-agent-server +``` + +## 3. Add the Backend in Agent Canvas + +With the container running, point Agent Canvas at it: + +1. Open Agent Canvas. +2. Click the backend switcher in the top bar and choose **Manage Backends**. +3. Click **Add Backend** and fill in: + - **Name** — anything memorable, e.g. `Local Docker (JDK)` + - **Host / Base URL** — `http://localhost:8000` + - **API Key** — the `SESSION_API_KEY` value from step 2 + - **Type** — `Local` +4. Save the backend and select it as the **active backend**. + +Agent Canvas will run a health check against the URL. Once it passes, the backend is ready to handle conversations using the tools you baked into the image. + + + See [Connect and Manage Backends](/openhands/usage/agent-canvas/backends) for what changes when you switch the active backend (settings, LLM availability, MCP servers, automations). + + +## Updating the Image + +When you want to add more tooling or pick up a newer agent server release: + +```bash +docker pull ghcr.io/openhands/agent-server:latest-python +docker build -t my-agent-server:latest . +docker stop openhands-agent-server && docker rm openhands-agent-server +# then re-run the `docker run` from step 2 +``` + +The existing backend entry in Agent Canvas keeps working as long as the host, port, and `SESSION_API_KEY` stay the same. + +## Related Guides + +- [Connect and Manage Backends](/openhands/usage/agent-canvas/backends) +- [Setup a Local Backend](/openhands/usage/agent-canvas/backend-setup/local) +- [Setup a VM Backend](/openhands/usage/agent-canvas/backend-setup/vm) +- [Agent Server Overview](/sdk/guides/agent-server/overview) From 5d4a81c689c88c4d1fe64bda3de3cb7af3ac9285 Mon Sep 17 00:00:00 2001 From: Robert Brennan Date: Tue, 2 Jun 2026 12:35:08 -0400 Subject: [PATCH 2/4] docs: use agent-server-java name, port 8001, add multi-backend section Co-authored-by: openhands --- .../agent-canvas/backend-setup/docker.mdx | 51 ++++++++++++++----- 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/openhands/usage/agent-canvas/backend-setup/docker.mdx b/openhands/usage/agent-canvas/backend-setup/docker.mdx index e35da608..771499c1 100644 --- a/openhands/usage/agent-canvas/backend-setup/docker.mdx +++ b/openhands/usage/agent-canvas/backend-setup/docker.mdx @@ -45,7 +45,7 @@ USER openhands Build the image with a tag you can reference later: ```bash -docker build -t my-agent-server:latest . +docker build -t agent-server-java:latest . ``` @@ -56,32 +56,34 @@ docker build -t my-agent-server:latest . Start the image, expose the agent server's HTTP port, and protect it with a session API key. The agent server reads `SESSION_API_KEY` from the environment and requires the same value on the `X-Session-API-Key` header for every request. +Use the same name for the container as you'll use for the Agent Canvas backend — it makes it easy to tell which container backs which backend later. + ```bash # Pick any sufficiently random value; you'll paste it into Agent Canvas later. export SESSION_API_KEY="$(openssl rand -hex 32)" docker run -d \ - --name openhands-agent-server \ - -p 8000:8000 \ + --name agent-server-java \ + -p 8001:8000 \ -e SESSION_API_KEY="$SESSION_API_KEY" \ - my-agent-server:latest + agent-server-java:latest ``` Verify the server is reachable: ```bash -curl -H "X-Session-API-Key: $SESSION_API_KEY" http://localhost:8000/health +curl -H "X-Session-API-Key: $SESSION_API_KEY" http://localhost:8001/health ``` - The agent server can execute arbitrary shell commands inside the container. Treat `SESSION_API_KEY` like a production credential and **do not expose port 8000 to the public internet** without a TLS-terminating reverse proxy in front of it. + The agent server can execute arbitrary shell commands inside the container. Treat `SESSION_API_KEY` like a production credential and **do not expose port 8001 to the public internet** without a TLS-terminating reverse proxy in front of it. To follow logs or stop the container: ```bash -docker logs -f openhands-agent-server -docker stop openhands-agent-server +docker logs -f agent-server-java +docker stop agent-server-java ``` ## 3. Add the Backend in Agent Canvas @@ -91,8 +93,8 @@ With the container running, point Agent Canvas at it: 1. Open Agent Canvas. 2. Click the backend switcher in the top bar and choose **Manage Backends**. 3. Click **Add Backend** and fill in: - - **Name** — anything memorable, e.g. `Local Docker (JDK)` - - **Host / Base URL** — `http://localhost:8000` + - **Name** — `agent-server-java` (matching the container name) + - **Host / Base URL** — `http://localhost:8001` - **API Key** — the `SESSION_API_KEY` value from step 2 - **Type** — `Local` 4. Save the backend and select it as the **active backend**. @@ -103,14 +105,39 @@ Agent Canvas will run a health check against the URL. Once it passes, the backen See [Connect and Manage Backends](/openhands/usage/agent-canvas/backends) for what changes when you switch the active backend (settings, LLM availability, MCP servers, automations). +## Running Multiple Custom Backends + +You can build as many custom images as you need and run each one as its own backend in Agent Canvas. A common pattern is one image per language or repo toolchain — for example a `agent-server-java` for JVM work, a `agent-server-rust` for Cargo projects, and a `agent-server-data` image with the Python data stack pre-installed. + +Give each container its own name, its own host port, and its own `SESSION_API_KEY`, then add a matching backend entry in Agent Canvas for each one: + +```bash +# JVM toolchain on port 8001 +docker run -d --name agent-server-java \ + -p 8001:8000 -e SESSION_API_KEY="$JAVA_KEY" \ + agent-server-java:latest + +# Rust toolchain on port 8002 +docker run -d --name agent-server-rust \ + -p 8002:8000 -e SESSION_API_KEY="$RUST_KEY" \ + agent-server-rust:latest + +# Python data stack on port 8003 +docker run -d --name agent-server-data \ + -p 8003:8000 -e SESSION_API_KEY="$DATA_KEY" \ + agent-server-data:latest +``` + +In **Manage Backends**, add one entry per container using the matching name, URL, and API key. You can then switch between them from the backend selector depending on what kind of task you're working on. + ## Updating the Image When you want to add more tooling or pick up a newer agent server release: ```bash docker pull ghcr.io/openhands/agent-server:latest-python -docker build -t my-agent-server:latest . -docker stop openhands-agent-server && docker rm openhands-agent-server +docker build -t agent-server-java:latest . +docker stop agent-server-java && docker rm agent-server-java # then re-run the `docker run` from step 2 ``` From 09e3114e3542a659903dd265aa98a8267b2a0f59 Mon Sep 17 00:00:00 2001 From: Robert Brennan Date: Tue, 2 Jun 2026 12:35:37 -0400 Subject: [PATCH 3/4] docs: pin agent server version in FROM example Co-authored-by: openhands --- openhands/usage/agent-canvas/backend-setup/docker.mdx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/openhands/usage/agent-canvas/backend-setup/docker.mdx b/openhands/usage/agent-canvas/backend-setup/docker.mdx index 771499c1..5be9e99d 100644 --- a/openhands/usage/agent-canvas/backend-setup/docker.mdx +++ b/openhands/usage/agent-canvas/backend-setup/docker.mdx @@ -20,7 +20,11 @@ The OpenHands Agent Server publishes a base image at `ghcr.io/openhands/agent-se The example below adds the JDK and a few common utilities. Save it as `Dockerfile`: ```dockerfile icon="docker" -FROM ghcr.io/openhands/agent-server:latest-python +# Pin to a specific agent server version for reproducible builds. +# Replace with a published tag, e.g. `0.10.0-python` or a commit +# SHA tag like `1a2b3c4-python`. See: +# https://github.com/OpenHands/software-agent-sdk/pkgs/container/agent-server +FROM ghcr.io/openhands/agent-server: USER root @@ -132,10 +136,9 @@ In **Manage Backends**, add one entry per container using the matching name, URL ## Updating the Image -When you want to add more tooling or pick up a newer agent server release: +When you want to add more tooling or pick up a newer agent server release, bump the pinned tag in the `FROM` line of your `Dockerfile`, then: ```bash -docker pull ghcr.io/openhands/agent-server:latest-python docker build -t agent-server-java:latest . docker stop agent-server-java && docker rm agent-server-java # then re-run the `docker run` from step 2 From 8d1cde0e0efce4d8dab44ac6348fc7e3bf3af0c5 Mon Sep 17 00:00:00 2001 From: Robert Brennan Date: Tue, 2 Jun 2026 12:37:15 -0400 Subject: [PATCH 4/4] docs: use 1-python base tag with short pinning tip Co-authored-by: openhands --- .../usage/agent-canvas/backend-setup/docker.mdx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/openhands/usage/agent-canvas/backend-setup/docker.mdx b/openhands/usage/agent-canvas/backend-setup/docker.mdx index 5be9e99d..9d1dd127 100644 --- a/openhands/usage/agent-canvas/backend-setup/docker.mdx +++ b/openhands/usage/agent-canvas/backend-setup/docker.mdx @@ -20,11 +20,7 @@ The OpenHands Agent Server publishes a base image at `ghcr.io/openhands/agent-se The example below adds the JDK and a few common utilities. Save it as `Dockerfile`: ```dockerfile icon="docker" -# Pin to a specific agent server version for reproducible builds. -# Replace with a published tag, e.g. `0.10.0-python` or a commit -# SHA tag like `1a2b3c4-python`. See: -# https://github.com/OpenHands/software-agent-sdk/pkgs/container/agent-server -FROM ghcr.io/openhands/agent-server: +FROM ghcr.io/openhands/agent-server:1-python USER root @@ -46,6 +42,10 @@ USER openhands The base image already ships with Python, Node.js, `git`, `jq`, `tmux`, build tools, and a VS Code Web + VNC stack. Only add what you actually need on top. + + For fully reproducible builds, pin to a more specific tag like `1.24.0-python` instead of `1-python`. + + Build the image with a tag you can reference later: ```bash @@ -136,9 +136,10 @@ In **Manage Backends**, add one entry per container using the matching name, URL ## Updating the Image -When you want to add more tooling or pick up a newer agent server release, bump the pinned tag in the `FROM` line of your `Dockerfile`, then: +When you want to add more tooling or pick up a newer agent server release: ```bash +docker pull ghcr.io/openhands/agent-server:1-python docker build -t agent-server-java:latest . docker stop agent-server-java && docker rm agent-server-java # then re-run the `docker run` from step 2