6.4 KiB
English | 日本語
Running MAESTRO with Docker
The fastest way to run MAESTRO is Docker Compose. This guide covers the full
path from git clone to a working instance, plus the things that commonly trip
people up (Linux networking, the LLM endpoint, data persistence, the sandbox).
TL;DR
Works on Linux and on Windows via WSL2 (Docker Desktop's WSL integration).
docker compose up -d # builds the image on first run, then starts
# open http://localhost:9876
No .env or config.yaml is required to start: a fresh docker compose up
opens a setup wizard in the UI where you point MAESTRO at your LLM. To preset the
endpoint instead, cp .env.example .env and set OLLAMA_BASE_URL/OLLAMA_MODEL
(see The LLM endpoint).
Compose publishes the UI on 9876 on all host interfaces, so a Docker host
is reachable from your LAN out of the box. That also means a fresh instance is
exposed and unauthenticated — anyone who can reach the port can run code on
the host via the agent's Bash tool. Read Going beyond localhost
before leaving it up on a shared network.
Windows (WSL2)
Run the same command inside a WSL2 distro with Docker Desktop's WSL integration
enabled — nothing else is needed. The headed-browser stack runs entirely inside
the container (no host X server or WSLg), and the repository's .gitattributes
keeps the build's shell scripts LF-only, so a Windows checkout (which would
otherwise convert them to CRLF) still builds cleanly.
What the container is (and is not)
- It runs the MAESTRO app (web UI, workers, tools, optional gateway).
- It does not run an LLM. You point MAESTRO at an existing OpenAI-compatible endpoint (Ollama, vLLM, a hosted gateway, …).
- The image bundles the headed-browser stack (Xvfb / x11vnc / Chromium) and the Bash sandbox (bubblewrap + a pre-baked Python toolchain), so the Browser tab and sandboxed Bash work out of the box.
The LLM endpoint
.env.example defaults to an Ollama running on the host:
OLLAMA_BASE_URL=http://host.docker.internal:11434/v1
OLLAMA_MODEL=qwen3:32b
host.docker.internal resolves to the host on Docker Desktop (macOS/Windows)
and — because docker-compose.yml sets extra_hosts: host.docker.internal:host-gateway
— on Linux too. You usually don't need to change anything.
Point it elsewhere when your LLM is not on the Docker host:
# Ollama / vLLM on another machine (use that machine's IP)
OLLAMA_BASE_URL=http://192.0.2.10:11434/v1
# A hosted OpenAI-compatible gateway
OLLAMA_BASE_URL=https://your-gateway.example.com/v1
Make sure the model in OLLAMA_MODEL is actually pulled/served by that
endpoint, or task runs will fail at the first LLM call.
Verify it's running
docker compose ps # STATUS should become healthy
docker compose logs -f maestro # watch startup; Ctrl-C to stop following
The container has a healthcheck that polls /health. Once it reports
healthy, open http://localhost:9876, create a task, and confirm it
progresses. If the LLM endpoint is wrong you'll see connection errors in the
logs as soon as a task starts.
Data persistence
Two named volumes survive docker compose down / image rebuilds:
| Volume | Mount | Holds |
|---|---|---|
maestro-data |
/app/data |
SQLite DB, users, skills, secrets |
maestro-workspaces |
/workspaces |
per-task agent workspaces |
docker compose down -v deletes these volumes (and all your data) — omit -v
to keep them.
Configuration
The image ships a runnable config.yaml (copied from config.yaml.example),
and .env overrides the common knobs (LLM endpoint, PORT). For most setups,
editing .env is all you need.
To manage the full config.yaml from the host (and have Settings UI edits
persist across container recreation), bind-mount it — uncomment in
docker-compose.yml:
volumes:
- ./config.yaml:/app/config.yaml # create ./config.yaml first
Create the host file before starting (cp config.yaml.example config.yaml), or
Docker will create a directory at that path. Without the bind-mount, Settings
UI changes live only inside the container and are lost when it is recreated.
The Bash sandbox in Docker
safety.bash_sandbox defaults to auto: use bubblewrap when available, fall
back to a hardened allowlist otherwise. The image installs bubblewrap, so the
sandbox is active by default. For multi-user or untrusted workloads set
safety.bash_sandbox: always (fail-closed if bubblewrap is unavailable).
bubblewrap needs unprivileged user namespaces. They are enabled on most modern
hosts; if your host or container runtime disables them, auto degrades to the
hardened allowlist and logs a warning at startup. See
operations/bash-sandbox-provisioning.md.
Build vs. prebuilt image
docker compose up builds the image locally from the Dockerfile (there is
no published image). The first build downloads Chromium and the Python
toolchain, so expect several minutes; later starts reuse the cached image.
Rebuild after pulling new code with docker compose up -d --build.
Going beyond localhost
By default Compose publishes 9876 on all interfaces, so the instance is already
reachable from your LAN — and unauthenticated. Before you rely on it on any
shared or untrusted network, harden it:
- Enable authentication (OAuth, or local accounts).
- Set
safety.bash_sandbox: always. - Terminate TLS — either MAESTRO's native HTTPS or a reverse proxy in front.
To keep an instance local-only instead (e.g. reach it over an SSH tunnel),
pin the compose port mapping back to 127.0.0.1:9876:9876.
See SECURITY.md and getting-started.md for the full hardening checklist.
Troubleshooting
| Symptom | Likely cause |
|---|---|
| Tasks fail immediately with a connection error | OLLAMA_BASE_URL unreachable from the container, or the model isn't served |
host.docker.internal not found (Linux) |
extra_hosts removed from compose, or an old Docker without host-gateway — use the host's LAN IP |
Settings changes vanish after down/up |
config.yaml not bind-mounted (see Configuration) |
| Browser tab / CAPTCHA pool unstable | /dev/shm too small — compose sets shm_size: 1gb; keep it |
| All data gone after restart | used docker compose down -v (the -v deletes volumes) |