Skip to main content

Installation

OtoDock is self-hosted: you run it on your own Linux server. This guide gets a working instance up and serving its dashboard with one short install script. When you're done, head to First run to create your account and chat with your first agent. (Developing OtoDock, or prefer to build what you run? See From source at the end.)

Prerequisites

  • A Linux server — Debian 12 or Ubuntu 22.04 LTS (or newer). 4 GB RAM is the minimum; more is recommended for multi-user installs and parallel agent sessions — especially if your agents use the heavier Docker MCP sidecars (the Camoufox browser, file tools). See How much RAM? below for what fits.
  • Docker and the Docker Compose plugin — if the server doesn't have them yet, install Docker Engine first (Debian/Ubuntu shortcut: curl -fsSL https://get.docker.com | sh, then sudo usermod -aG docker $USER and log back in). The install script checks for it and stops with the same instructions if it's missing.

That's all — the OtoDock image ships with everything else baked in (the AI CLIs, the sandbox runtime, the toolchain agents use).

Install

Create a folder for the install, then run the script in it:

mkdir otodock && cd otodock
curl -fsSLO https://raw.githubusercontent.com/OtoDock/oto-dock/main/scripts/install.sh
bash install.sh

The installer is a short, commented script built to be read. It checks Docker, writes a .env with a generated database password, handles the Ubuntu 24.04+ host step automatically when the host needs it (the one step that may ask for sudo), downloads the release-pinned docker-compose.yml, and starts the stack. Everything lands in the folder you run it from, and it performs fresh installs only — it stops rather than touch an existing install.

That one .env file is the whole configuration: on first boot OtoDock generates its remaining secrets and appends them there, your data lives in named Docker volumes, and the compose file pins the release it shipped with — upgrading later is a one-line version bump (see Upgrading & backups). Every optional knob is documented in the Configuration reference.

When it finishes, skip ahead to Verify it's running. If your users will browse to the server by name or IP rather than localhost, set the public URL in the generated .env (its comments show where):

# .env
DASHBOARD_PUBLIC_URL=http://your-server:8400 # the address your users browse to
TZ=Europe/Athens # optional — scheduled tasks and
# notification times use this

…then apply it:

docker compose up -d
Single sign-on

If your team signs in through an identity provider (Authentik, Keycloak, Entra ID, …), add the OIDC_* settings to the same .envUsers & Access shows the exact lines. You can also do this later; local accounts work out of the box.

How much RAM?

OtoDock admits agent sessions based on live free memory, so a full box politely asks you to retry instead of crashing — but the ceiling is set by your RAM. Rough guide for concurrent Claude Code / Codex CLI sessions (each grows to roughly 1 GB with a full context; the same numbers apply to Docker Compose and source installs):

Server RAMConcurrent sessions…alongside a heavy Docker MCP (e.g. the browser)
4 GB~3~2
8 GB~5–6~4–5
16 GB~10–12~9–11
32 GB20+19+

The estimates behind these numbers are tunable in .env (see the comments in config.env.example).

Verify it's running

OtoDock serves both its API and dashboard on port 8400 by default.

curl http://localhost:8400/health # → ok

Then open http://<your-server>:8400 in a browser. A fresh install greets you with the setup wizard — continue to First run.

Day-to-day, two commands cover operations (run them next to the compose file):

docker compose logs -f otodock-proxy # follow the server logs
docker compose down # stop the stack (your data stays in its volumes)

Reverse proxy & HTTPS

For anything beyond a local trial, front port 8400 with a reverse proxy (Caddy, nginx, Traefik) or a tunnel that terminates TLS. Forward the standard headers on every hop — at minimum Host, plus X-Forwarded-Proto from the hop that terminates TLS — so OtoDock knows how the browser reached it. Then tell OtoDock about the proxy in .env:

# .env — both required when OtoDock sits behind a reverse proxy
DASHBOARD_PUBLIC_URL=https://otodock.example.com # the public address
TRUSTED_PROXY=10.0.0.5 # your reverse proxy's IP

DASHBOARD_PUBLIC_URL drives login cookies, OAuth redirects, and links in notifications — and with it set, in-chat artifacts and mini-apps stay correctly sandboxed on that address even if a hop drops X-Forwarded-Proto. TRUSTED_PROXY (plain IPs or CIDRs, comma-separate several) lets OtoDock see each visitor's real address through the proxy — the login protections (rate limits, the local-network-only sign-in restriction) depend on it.

Reverse proxies with forced login (SSO / forward-auth)

If you put OtoDock behind a gateway that forces a login on every request (Authentik, Authelia, oauth2-proxy, Cloudflare Access), a few paths must skip that login — the clients calling them authenticate themselves and can't complete a browser login:

  • ^/ui-kit/ — the styling and chart libraries that in-chat artifacts and mini-apps load. These render in a hard security sandbox that deliberately sends no cookies, so a forced-login gateway silently blocks them and agent-built dashboards come out unstyled. The files are public static assets (fonts, CSS, chart libraries) — safe to exempt.
  • ^/v1/webhooks/ — every incoming trigger fire and every integration event (the vendor webhook receivers). OtoDock authenticates these itself with keys and signed requests.
  • /ws/ — the live dashboard connection. Make sure your proxy forwards WebSocket upgrades; with strict forced-login gateways, exempt this path from the login redirect too (a WebSocket handshake can't follow one) — OtoDock authenticates the socket itself with your logged-in session.
  • ^/v1/images/temp/ — only if you use reverse image search: the search vendor fetches the photo through a short-lived, random tokenized URL and has no browser session.
  • /favicon.png — the browser fetches the icon before the login redirect completes; a public static icon, safe to exempt.

Everything else — the dashboard and all management endpoints — stays behind your gateway. The full recipe, including a couple of niche paths some MCPs use, is in API & Webhooks.

Private network ranges

OtoDock pins its own internal Docker subnets, so no host Docker daemon changes are needed. If the defaults (10.200.0.0/24 and 10.201.0.0/16) overlap your LAN or VPN, override OTODOCK_NETWORK_SUBNET / OTODOCK_MCP_ADDRESS_POOL in .env.

From source (developers & contributors)

Everything above runs the published images. If you're developing OtoDock — or just prefer to build what you run — clone the repository and use the source flow.

Containerised, built from source

Needs only Git and Docker — everything builds inside the containers:

git clone https://github.com/OtoDock/oto-dock.git
cd oto-dock
printf 'POSTGRES_PASSWORD=%s\n' "$(openssl rand -hex 24)" > config.env
scripts/compose.sh up -d --build

scripts/compose.sh stacks the build overlay (docker-compose.build.yml) on the same base compose file, with the base images pinned from VERSIONS.md. It builds exactly what your working tree contains — so updating is just git pull and re-running the same command. The source flow keeps its settings in config.env at the repository root (same contents and behaviour as .env above).

Native (bare-metal development)

The server can also run directly on the host — this is how OtoDock itself is developed. One idempotent bootstrap script stands up the whole environment: the pinned toolchain from VERSIONS.md (Python, Node, uv, pnpm), the sandbox runtime, Docker, the server's virtualenv and config.env, PostgreSQL in a container (loopback-only), and a dashboard build:

git clone https://github.com/OtoDock/oto-dock.git
cd oto-dock
scripts/dev-setup.sh

Then run it — foreground while you're hacking on the code, so logs and restarts are right in your terminal:

cd proxy && venv/bin/python app.py # the server (serves the built dashboard too)
cd dashboard && npm run dev # optional second terminal: hot-reload dashboard

If the box doubles as a long-running instance, register it as a systemd service instead — the bootstrap generates the unit for your checkout path and user, enables it, and starts it:

scripts/dev-setup.sh --service

Updating is git pull, re-running scripts/dev-setup.sh (it reconciles the toolchain and dependencies, never overwrites your secrets), and restarting the server.

Hard disk quotas

Per-agent storage quotas enforce limits as warnings on any setup, but hard enforcement (blocking writes past a cap) needs a native install on an XFS volume with project quotas. A Docker Compose deployment always uses the warning-only tier. See Usage & limits.

Next steps