AI Coding AgentsDockerSelf-Hosting

OpenHands with Docker: a self-hosted AI coding agent with a local model (Linux, Windows, Mac)

Before you start

  • 20 min read

Boxes marked Run in terminal are what you type; plain text explains why. Check what you need first

What you get in one minute#

OpenHands is a free, open-source AI coding agent with a web page: you describe a task, and the agent edits files, runs shell commands and shows its work, all inside one Docker container on your own computer.

OpenHands official logo

Logo: the official OpenHands project (github.com/OpenHands/OpenHands)

The project now calls the web interface Agent Canvas and marks the old "local GUI" as legacy. This guide follows the current Docker route. The agent only sees the folders you mount, and the model can be a local one served by Ollama (see our Ollama guide), so your code need not leave your machine.

WhatValue (official docs, read October 2026)
Current versionAgent Canvas / OpenHands v1.34.0, released October 2026 (latest)
Image to pinghcr.io/openhands/agent-canvas:1.34.0 (tag checked on the registry; Linux amd64 and arm64)
Time neededAbout 20 minutes without the model download (our estimate, not measured)
Size and RAMNot documented for the container. The docs call a VM with 2 vCPU and 4 GB RAM enough for one user
Local model hardwareThe docs name 24 GB VRAM, or a Mac with 64 GB memory, for their recommended model
PortsTCP 8000 on your computer only (127.0.0.1); the page is at /canvas
LicenceMIT (LICENSE and package.json in the repository)
Official docsdocs.openhands.dev and github.com/OpenHands/OpenHands

At a glance:

  • What it is: a browser page plus an agent server in one container, on Linux, Windows or Mac (Docker Desktop).
  • The Docker socket: the current image does not mount it. The legacy app did, and one docs example still shows it (see below).
  • Local models: the docs recommend Qwen3.6-35B-A3B, which needs serious hardware. Small models often act like a plain chatbot.
  • Privacy: the first setup screen defaults to a hosted model. Choose a local endpoint to keep prompts and code on your machine.
  • Honest limit: the agent can run any shell command inside its container. Treat it like a new colleague with a keyboard.

How it fits together#

Your browser talks to one container on 127.0.0.1:8000. The container reaches the model, and only two folders are shared with it.

Left to right: your browser opens localhost 8000 canvas, which reaches one OpenHands container with no Docker socket mounted; the container reads and writes a projects folder, saves settings in a Docker volume, and sends prompts to Ollama on your computer, or optionally (dashed) to a hosted model provider outside your machine

Drawn for this guide from the official Docker docs. The dashed arrow is optional and is the one that leaves your machine.

Before you start#

You need Docker with Compose and a browser. First, the ways to run it, so you see why this guide picks Docker:

MethodAgent can reachOur view
Docker, one container (this guide)Only the mounted foldersStart here
Docker, one container per conversationEach conversation's workspaceOptional, Step 6
npm install -g @openhands/agent-canvas or npxYour whole user accountThe docs warn: "full access to your filesystem"
Desktop app (preview build)Your whole user accountEarly preview, not covered

Linux: Docker Engine with the Compose plugin, curl, an x86_64 or arm64 machine.

Run in terminalbash
docker --version
docker compose version

Windows: Docker Desktop for Windows, started, with PowerShell.

Run in terminalpowershell
docker --version
docker compose version

Mac: Docker Desktop for Mac (Apple silicon or Intel), started, with Terminal.

Run in terminalbash
docker --version
docker compose version

Checklist:

  • [ ] Docker running (docker ps shows a table, not an error)
  • [ ] Port 8000 free on your computer
  • [ ] A model plan: local Ollama or a hosted provider key
  • [ ] For the HTTPS section only: a domain with a DNS record and ports 80 and 443 free

Tested by DIY Lab Hub: not yet. The commands come from the official README at the v1.34.0 tag and the docs pages read in October 2026. The Compose file is our translation of the official docker run command. We did not run it.

Step 1: Create the folders#

Everything lives under ~/docker_volumes/openhands. The projects folder is the only part of your disk the agent will see.

Linux#

Run in terminalbash
mkdir -p ~/docker_volumes/openhands/projects
cd ~/docker_volumes/openhands

Windows (PowerShell)#

Codepowershell
New-Item -ItemType Directory -Force -Path "$HOME\docker_volumes\openhands\projects" | Out-Null
Set-Location "$HOME\docker_volumes\openhands"

Mac (Terminal)#

Same commands as Linux:

Run in terminalbash
mkdir -p ~/docker_volumes/openhands/projects
cd ~/docker_volumes/openhands

Check it worked. ls -d ~/docker_volumes/openhands/projects (Linux, Mac) or Test-Path "$HOME\docker_volumes\openhands\projects" (Windows, prints True).

Tip: Put only the projects you are happy to hand over in this folder. Never your home folder, never a folder with .env files, SSH keys or password exports. The agent can read everything in it.

Step 2: Write the Compose file and start OpenHands#

The official command is one docker run; this Compose file maps its flags one to one (pinned image, port on 127.0.0.1 only, /projects mount, settings volume). Settings go in a named Docker volume, not in ~/docker_volumes, because the docs warn that a host folder there can cause unable to open database file on Linux (the container user is UID 10001). extra_hosts is the docs' --add-host host.docker.internal:host-gateway, needed in Step 4. AGENT_CANVAS_DISABLE_TELEMETRY=1 is the documented telemetry switch.

Linux#

Run in terminalbash
cd ~/docker_volumes/openhands
cat > docker-compose.yml << 'EOF'
services:
  openhands:
    image: ghcr.io/openhands/agent-canvas:1.34.0
    container_name: openhands
    restart: unless-stopped
    tty: true
    stdin_open: true
    ports:
      - "127.0.0.1:8000:8000"
    environment:
      - AGENT_CANVAS_ALLOW_LAN_SESSION_KEY=true
      - AGENT_CANVAS_DISABLE_TELEMETRY=1
    extra_hosts:
      - "host.docker.internal:host-gateway"
    volumes:
      - openhands-data:/home/openhands/.openhands
      - ./projects:/projects

volumes:
  openhands-data:
    name: openhands-data
EOF
docker compose up -d
docker compose ps
docker compose logs --tail=50 openhands

Windows (PowerShell)#

Create the same file with an editor, then start it. Save it as UTF-8:

Codepowershell
Set-Location "$HOME\docker_volumes\openhands"
notepad docker-compose.yml

Paste the YAML from the Linux block into Notepad and save. Then:

Run in terminalpowershell
docker compose up -d
docker compose ps
docker compose logs --tail=50 openhands

Mac (Terminal)#

Same commands as Linux (the cat > docker-compose.yml block, then docker compose up -d).

Check it worked. docker compose ps shows openhands as running. The docs publish no expected log text for this image, so read the log for errors rather than for a specific line. Then test the page:

Run in terminalbash
curl -I http://localhost:8000/canvas

On Windows use curl.exe -I http://localhost:8000/canvas. You should get an HTTP status line, not "connection refused".

Tip: AGENT_CANVAS_ALLOW_LAN_SESSION_KEY=true lets the page log itself in without a key. That is only safe because the port is published on 127.0.0.1. The README says to omit it whenever you publish the port on a network, as the HTTPS section does.

Step 3: Open the page and finish first-time setup#

Linux#

Run in terminalbash
xdg-open http://localhost:8000/canvas

Windows (PowerShell)#

Codepowershell
Start-Process "http://localhost:8000/canvas"

Mac (Terminal)#

Run in terminalbash
open http://localhost:8000/canvas

The wizard asks you to choose an agent, check the backend, set up the LLM and start a workflow. Choose the OpenHands agent. The backend step shows "You're connected!" for 127.0.0.1:8000. At the LLM step do not accept the default if you want to stay local: it is the hosted OpenHands service. Do Step 4, or use Settings > LLM later.

Check it worked. The backend shows as connected.

Step 4: Connect a local model with Ollama#

The container must reach Ollama as host.docker.internal, so Ollama listens on all interfaces. The docs also set a large context, because "the default (4096) is way too small - not even the system prompt will fit". For installing Ollama, see our Ollama guide.

The docs recommend Qwen3.6-35B-A3B, an open-weight model built for agentic coding. They say quantised builds need a GPU with at least 24 GB of VRAM, or an Apple Silicon Mac with at least 64 GB of unified memory.

Linux#

If Ollama already runs as a service, set the same variables on that service first (the Ollama guide shows how). Otherwise:

Run in terminalbash
OLLAMA_CONTEXT_LENGTH=32768 OLLAMA_HOST=0.0.0.0:11434 OLLAMA_KEEP_ALIVE=-1 nohup ollama serve &
ollama pull qwen3.6:35b-a3b

Windows (PowerShell)#

The docs show only the Linux and Mac line. This is our translation of it (not run). Quit the Ollama tray app first, then in one window:

Codepowershell
$env:OLLAMA_CONTEXT_LENGTH = "32768"
$env:OLLAMA_HOST = "0.0.0.0:11434"
$env:OLLAMA_KEEP_ALIVE = "-1"
ollama serve

In a second window:

Run in terminalpowershell
ollama pull qwen3.6:35b-a3b

Mac (Terminal)#

Same commands as Linux:

Run in terminalbash
OLLAMA_CONTEXT_LENGTH=32768 OLLAMA_HOST=0.0.0.0:11434 OLLAMA_KEEP_ALIVE=-1 nohup ollama serve &
ollama pull qwen3.6:35b-a3b

Now tell OpenHands. In the page open Settings > LLM > Advanced and enter:

FieldValue
Custom Modelopenai/qwen3.6:35b-a3b (the prefix openai/ plus the model name Ollama serves)
Base URLhttp://host.docker.internal:11434/v1
API Keyany placeholder, for example local-llm (Ollama does not check it)

Save, start a new conversation and say hello.

Check it worked. On your computer, curl http://127.0.0.1:11434/v1/models lists the model (Windows: curl.exe). In OpenHands, a short message gets an answer. The docs also check from inside the container with docker exec <container> curl -s http://host.docker.internal:11434/v1/models (their example uses the legacy container; we did not confirm curl is in this image).

Tip: OLLAMA_HOST=0.0.0.0 makes Ollama answer other devices on your network too, and it has no login. Allow port 11434 only from your own machine in your firewall, or keep the computer on a network you trust.

For weaker hardware the docs name two community-reported models, qwen2.5-coder-14b-instruct and qwopus3.5-27b-v3 Q8_0. We did not verify matching Ollama tags, and the docs give no hardware figures for them. The other option is a hosted model; set spending limits.

Step 5: Run a first task in a safe folder#

Make a throwaway project and give the agent a small job.

Linux#

Run in terminalbash
mkdir -p ~/docker_volumes/openhands/projects/hello

Windows (PowerShell)#

Codepowershell
New-Item -ItemType Directory -Force -Path "$HOME\docker_volumes\openhands\projects\hello" | Out-Null

Mac (Terminal)#

Same command as Linux:

Run in terminalbash
mkdir -p ~/docker_volumes/openhands/projects/hello

In the page choose Open Workspace and pick /projects/hello before you start the conversation. Then ask: "Create hello.py that prints hello, and run it."

Check it worked. hello.py appears in the folder on your computer, outside the container:

Run in terminalbash
ls ~/docker_volumes/openhands/projects/hello

On Windows: Get-ChildItem "$HOME\docker_volumes\openhands\projects\hello". If the agent cannot write on Linux, the folder owner may not match the container user (UID 10001). The docs mention that UID only for the settings folder, so this is our guess, not a documented fix.

Tip: Always use Open Workspace first. A conversation started without it works in a folder inside the container that is not saved when the container is recreated (open issues 18960 and 19019).

Step 6 (optional): One container per conversation#

The second Docker mode, new in v1.34.0: Agent Canvas runs on your computer and each conversation gets its own hardened container. It needs Node.js 24 or later, uv and a user who may run docker, so read the next section first. Stop the all-in-one container to free port 8000: docker compose down.

Linux#

Run in terminalbash
node --version
uv --version
npm install -g @openhands/agent-canvas
OH_CONVERSATION_RUNTIME=docker agent-canvas

Then open http://localhost:8000, not /canvas.

Windows (PowerShell)#

Not supported natively ("uses POSIX user/group IDs", says the README). Use Linux under WSL 2 with Docker Desktop's WSL integration, install Node.js 24 and uv inside it, check docker info, and run the Linux commands there.

Mac (Terminal)#

Same commands as Linux, with Docker Desktop running:

Run in terminalbash
npm install -g @openhands/agent-canvas
OH_CONVERSATION_RUNTIME=docker agent-canvas

Check it worked. After you start a conversation, docker ps --filter name=agent-server-conversation- lists its container. Ask the agent to run id and pwd: the docs expect /workspace as the working directory.

Each container defaults to 4 GB memory and 2 CPUs and stops after 20 minutes idle. In this mode the launcher listens on all interfaces and does not auto-login, so use your firewall and the API key. Issue 19066 (open) reports the page may not update live until you reload it.

The Docker socket and the sandbox: what the risk is#

The Docker socket (/var/run/docker.sock) is the control line to the Docker daemon. Docker's own security page says only trusted users should control the daemon, because anyone who does can start a container with your host's / mounted and change your host files without restriction. Handing the socket to an AI agent therefore hands it your computer.

SetupSocket?What the agent can reach
This guide: one containerNo/projects, the settings volume, the container itself, the network the container can use
This guide with --privileged (docs: "Let the Agent Use Docker")No, but broad kernel accessThe docs say it "substantially weakens the isolation"; they offer "no safer middle ground"
Step 6: container per conversationHost Docker used by agent-canvasThe agent runs in a container with all capabilities dropped, no-new-privileges, host UID and API on 127.0.0.1; the outer process runs as you and can use Docker
Legacy app (openhands:1.8)Yes, -v /var/run/docker.sock:/var/run/docker.sockStill shown in the local-LLM page of the docs. Do not copy it

Use the first row. The docs describe no limit on the container's outgoing network, so assume the agent can reach the internet and services such as Ollama.

Put it behind HTTPS (with or without Traefik)#

This section is for a server with a domain, or a home name you want a padlock on. With only localhost, skip it. The agent runs shell commands, so the docs ask for a firewall and an API key before you expose it. The commands run on the machine that hosts OpenHands; Windows and Mac users only need a browser.

Two changes in both routes. Remove the published port and AGENT_CANVAS_ALLOW_LAN_SESSION_KEY (with the key injected, anyone reaching the page is logged in), and set a strong LOCAL_BACKEND_API_KEY, which the page then asks for. The docs generate one with openssl rand -base64 32.

Run in terminalbash
cd ~/docker_volumes/openhands
docker network create proxy
printf 'LOCAL_BACKEND_API_KEY=%s\n' "$(openssl rand -base64 32)" > .env
chmod 600 .env

If the network already exists, that is fine. Copy the key from .env into a password manager.

With Traefik#

On a server that already runs Traefik v3 with the Docker provider, replace the Compose file with this one. It assumes an external network named proxy and an entrypoint named https; use yours.

File contentyaml
services:
  openhands:
    image: ghcr.io/openhands/agent-canvas:1.34.0
    container_name: openhands
    restart: unless-stopped
    tty: true
    stdin_open: true
    environment:
      - AGENT_CANVAS_DISABLE_TELEMETRY=1
      - LOCAL_BACKEND_API_KEY=${LOCAL_BACKEND_API_KEY}
    extra_hosts:
      - "host.docker.internal:host-gateway"
    volumes:
      - openhands-data:/home/openhands/.openhands
      - ./projects:/projects
    networks:
      - proxy
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=proxy"
      - "traefik.http.routers.openhands.rule=Host(`openhands.<your-domain>`)"
      - "traefik.http.routers.openhands.entrypoints=https"
      - "traefik.http.routers.openhands.tls=true"
      - "traefik.http.services.openhands.loadbalancer.server.port=8000"

volumes:
  openhands-data:
    name: openhands-data

networks:
  proxy:
    external: true

Run docker compose up -d. Check it worked. curl -I https://openhands.<your-domain>/canvas returns an HTTP status line over HTTPS, and the page asks for the API key.

Without Traefik: Caddy#

Choose this if you have no Traefik. Caddy gets and renews its own certificates. Two cases:

CaseWhat you do
Public domainA DNS A record to the server, ports 80 and 443 open. Caddy gets a Let's Encrypt certificate itself
Home only, no domainUse tls internal (Caddy's own local certificate) and trust Caddy's root certificate on each device

Use the same Compose file as in the Traefik route, but delete the labels: block. Then create the Caddy folder and file:

Run in terminalbash
mkdir -p ~/docker_volumes/caddy/{data,config}
cd ~/docker_volumes/caddy

Save as ~/docker_volumes/caddy/docker-compose.yml (caddy:2.11.7 is the latest Caddy release read in October 2026):

File contentyaml
services:
  caddy:
    image: caddy:2.11.7
    container_name: caddy
    restart: unless-stopped
    ports:
      - "80:80/tcp"
      - "443:443/tcp"
      - "443:443/udp"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - ./data:/data
      - ./config:/config
    networks:
      - proxy

networks:
  proxy:
    external: true

Save as ~/docker_volumes/caddy/Caddyfile. The remote_ip lines follow the docs' advice to restrict 443 to your own address; replace the placeholder, or drop those two lines at home:

File contenttext
openhands.example.com {
    @outside not remote_ip <your-public-ip>
    respond @outside 403
    reverse_proxy openhands:8000
}

Home-only variant:

File contenttext
openhands.home.arpa {
    tls internal
    reverse_proxy openhands:8000
}

Start both stacks:

Run in terminalbash
cd ~/docker_volumes/caddy && docker compose up -d
cd ~/docker_volumes/openhands && docker compose up -d

For tls internal, install Caddy's root certificate on each device you browse from. Find it, copy it out, and import it (the OS steps are not from the OpenHands or Caddy docs: Windows "Trusted Root Certification Authorities", macOS Keychain Access with "Always Trust", Linux your distribution's CA store):

Run in terminalbash
docker exec caddy find /data -name root.crt
docker cp caddy:<path-from-find> ./caddy-root.crt

Check it worked. curl -I https://openhands.example.com/canvas returns an HTTP status line over HTTPS. For a home name the browser shows a padlock only after the root is trusted.

Tip: The Canvas page is under /canvas, so the address is https://openhands.<your-domain>/canvas. The bare domain may show nothing useful.

Security and testing#

ControlHow
Where the port listens127.0.0.1:8000 in Step 2. Never 8000:8000 on a machine on a network
API keyRequired once the port is reachable by others: LOCAL_BACKEND_API_KEY in .env, chmod 600
FirewallOnly 22, 80 and 443 open, per the docs' VM guide; 8000, 18000, 18001 and 3001 stay closed
Shared editor originIssue 16492 (listed in the docs): the bundled editor shares the page's origin and could read stored session keys. Open no untrusted content there

Quick tests (Linux, Mac; same in PowerShell):

Run in terminalbash
cd ~/docker_volumes/openhands
docker compose ps
docker compose logs --tail=100 openhands
docker inspect openhands --format '{{json .Mounts}}'

The last line should list only the settings volume and /projects, and no docker.sock.

What leaves your machine#

DataGoes where
Your prompts, file contents the agent reads, command outputTo the model you chose. With Ollama on your computer, nowhere. With a hosted provider or the default hosted OpenHands model, to that provider
Claude Code, Codex or Gemini CLI used through ACPTo those vendors, using their own login
Product telemetryOff with AGENT_CANVAS_DISABLE_TELEMETRY=1. The pages read do not say what it contains

Common mistakes#

MistakeWhy it happensDo this instead
Starting a conversation without choosing a workspaceThe agent then works in a folder inside the container. Issues 18960 and 19019 (both open) report the files vanish when the container is recreatedUse Open Workspace and pick /projects/<name> first
Binding a host folder to ~/.openhands on LinuxThe container user is UID 10001; the docs warn of unable to open database fileUse the named volume, as in Step 2
127.0.0.1 as the model addressInside the container that means the container itselfhttp://host.docker.internal:11434/v1, with extra_hosts on Linux
Leaving Ollama at its default contextThe docs say 4096 is too small even for the system promptSet OLLAMA_CONTEXT_LENGTH=32768
Using a small model and blaming OpenHandsThe docs say a weak model can act like a plain chatbot or fail tool callsTry the recommended or a community-reported model before debugging the setup
Model name without the openai/ prefixThe error is LLM Provider NOT providedopenai/<served-model-id> for Ollama, LM Studio, vLLM and SGLang
Publishing port 8000 on every interfaceThe page can log itself in when the session key is injectedKeep 127.0.0.1:8000; behind HTTPS drop AGENT_CANVAS_ALLOW_LAN_SESSION_KEY, set LOCAL_BACKEND_API_KEY
Adding --privileged so the agent can run DockerIt is the documented way, but it weakens the isolationAdd it only when needed, on a machine you can afford to lose
Running latest after a broken image publishThe docs' crash-loop page describes ERR_MODULE_NOT_FOUND from a bad latestPin 1.34.0, and pin an older tag such as 1.26.0 if a new one fails
AI assistants: giving the old commandsOlder guides and answers built on them show the legacy app: image docker.openhands.dev/openhands/openhands:1.8, port 3000, the docker.sock mount, or openhands serve. The docs now mark it "Local GUI (Legacy)"Use ghcr.io/openhands/agent-canvas:1.34.0, port 8000, page /canvas
AI assistants: a model name like ollama/qwen3.6:35b-a3bThe ollama/ prefix is common in other toolsThis setup uses openai/qwen3.6:35b-a3b and the /v1 base URL

Tip: Before you paste any OpenHands command from a forum or an AI chat, look for docker.sock and port 3000. Both mean the old app, and the socket is the part you do not want to hand over.

Tip: Test a new model on one small task in the hello folder before a real repository. If it only chats, it will not edit files.

Troubleshooting#

ProblemSolution
port is already allocated or port 8000 busyChange the Compose port to "127.0.0.1:3000:8000" and open http://localhost:3000/canvas
Container restarts in a loop with Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/opt/agent-canvas/<name>.mjs'The image was published broken (docs). Pin an earlier tag such as 1.26.0, then update once a fix is out
Page loads but "backend disconnected" or wrong keyCheck Manage Backends. The generated key: docker exec openhands sh -c 'cat "$STATE_DIR/api-key.txt"' (README)
LLM Provider NOT providedUse openai/<model> in Settings > LLM > Advanced and check the Base URL
Model cannot be reachedThe URL must work from the container: host.docker.internal, Ollama on 0.0.0.0, extra_hosts on Linux
The agent only chats, ignores filesContext too small or model too weak for tools. Raise the context, then change the model
error creating default "bridge" network: operation not permittedThe agent tried to start containers. By default the container has no Docker daemon. See the socket section before using --privileged
Terminal fails with TmuxObjectDoesNotExistIssue 18982 (open): the agent server runs without a UTF-8 locale. The issue's analysis points at LANG and LC_ALL; setting a UTF-8 value is our reading, not a documented fix
502 Could not create conversation container in Step 6Issue 18584 (open): the per-conversation mode fails on a rootless Docker daemon. Use the all-in-one container or a rootful daemon

Known bugs (open, October 2026)#

  • 18960, 19019: files outside /projects are lost when the container is recreated.
  • 19066: per-conversation mode may not update the page until you reload.
  • 18847: after connecting GitHub, the sandboxed agent reported no credentials and could not push.
  • 18584, 18982: rootless Docker in Step 6; terminal error without a UTF-8 locale.

Diagnostics: docker compose ps, docker compose logs --tail=200 openhands, docker inspect openhands, docker stats openhands.

Updates and backups#

Update. Read the release notes, change the tag in docker-compose.yml, then:

Run in terminalbash
cd ~/docker_volumes/openhands
docker compose pull
docker compose up -d
docker compose logs --tail=100 openhands

Recreating the container drops everything outside /projects and the settings volume.

Back up. The settings volume holds your settings, secrets and conversation history. Stop the container, archive the volume and your projects, and start again. The archive holds secrets, so chmod 600:

Run in terminalbash
cd ~/docker_volumes/openhands
docker compose down
mkdir -p backups
docker run --rm -v openhands-data:/data:ro -v "$PWD/backups:/backup" alpine:3 \
  tar -czf "/backup/openhands-data-$(date +%F).tar.gz" -C /data .
tar -czf "backups/openhands-projects-$(date +%F).tar.gz" projects
chmod 600 backups/*.tar.gz
docker compose up -d

On Windows, use PowerShell with -v "${PWD}\backups:/backup" and "/backup/openhands-data-$(Get-Date -Format yyyy-MM-dd).tar.gz"; Windows 10 and 11 ship tar for the projects. On Mac use the Linux commands.

Schedule. Linux and Mac: a crontab entry at 03:00 that runs the same lines. Windows: a Task Scheduler task that runs a .ps1 file with them. Keep a copy off the machine.

Restore test. Unpack into a new empty folder, never over the live one:

Run in terminalbash
mkdir -p /tmp/restore-test && tar -xzf backups/openhands-data-<date>.tar.gz -C /tmp/restore-test
ls /tmp/restore-test

Performance and resources#

The docs publish no RAM, CPU or disk figures for the container. They call a VM with 2 vCPU and 4 GB RAM enough for one user. The model is what costs: the recommended one needs 24 GB of VRAM or a 64 GB Apple Silicon Mac.

  • Measure: docker stats openhands for the container, ollama ps for the model.
  • Keep the model loaded: OLLAMA_KEEP_ALIVE=-1 (from the docs) avoids reload waits.
  • Step 6 limits: OH_CONVERSATION_CONTAINER_MEMORY (default 4g), OH_CONVERSATION_CONTAINER_CPUS (2.0), OH_CONVERSATION_CONTAINER_PIDS_LIMIT (512). A client can ask for more, so they are no hard cap on a shared server.

FAQ#

Is OpenHands free? The software is MIT licensed. A hosted model costs money, because OpenHands sends many prompts. A local model costs only hardware and power.

Is it the same as "Agent Canvas"? Yes. The repository now ships the Agent Canvas interface, which runs the OpenHands agent. It can also drive Claude Code, Codex, Gemini CLI, Pi and OpenCode through ACP, using their own logins.

Does it need a GPU? The agent does not. A good local model does: 24 GB of VRAM or a 64 GB Mac for the recommended one.

Is the Docker route safe? Safer than running it on your whole account, not risk-free. The agent can run any command in the container and read all of /projects. Keep the mount small, the port on 127.0.0.1, skip --privileged and never mount the Docker socket.

Does it work offline? The container and a local Ollama model need no cloud account. You need internet once, to pull the image and the model.

Sources#

Checked against the official docs on 2026-10-30.