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. When you're done, head to First run to create your account and chat with your first agent.

Prefer to watch first? Here's the whole install, uncut, with an honest clock — under three minutes from empty directory to the dashboard:

Two ways to install

MethodWhat it isBest for
Docker Compose (recommended)The official OtoDock images from GHCR, brought up with one compose file and one command. No repository checkout needed.Everyone. The fastest, most reproducible path.
From sourceClone the repository and either build the same container stack locally, or run the server natively on the host.Developers and contributors.

Both run the same software.

Prerequisites

  • A Linux server — Debian 12 or Ubuntu 22.04 LTS (or newer). 4 GB RAM is a comfortable minimum — see How much RAM? below for what fits.
  • Docker and the Docker Compose plugin.

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

Step 1 — Get the compose file

Pick a directory for your install and download the compose file into it:

mkdir -p /opt/otodock && cd /opt/otodock
curl -fsSLO https://raw.githubusercontent.com/OtoDock/oto-dock/main/docker-compose.yml

Step 2 — Create your .env

Create a .env file next to the compose file. This one file is OtoDock's configuration: Compose reads it automatically (no --env-file flag needed), and the server keeps its own generated secrets in it too.

cat > .env <<EOF
# Required — the bundled PostgreSQL initialises with this password on first run.
POSTGRES_PASSWORD=$(openssl rand -hex 24)

# The URL your users will browse to. Drives login cookies, OAuth redirects,
# document preview, and links in notifications. Leave as-is for a same-host trial.
DASHBOARD_PUBLIC_URL=http://localhost:8400

# Container timezone — scheduled tasks and notification times use this.
TZ=Europe/Athens
EOF
chmod 600 .env

That is genuinely all most installs need. On first boot OtoDock generates its remaining secrets (API key, signing keys, push-notification keys) and appends them to this same file, so they survive restarts and upgrades. Every other knob is optional and documented in the Configuration reference.

Single sign-on

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

Step 3 — Start it

docker compose up -d

The first run pulls the pinned release images and starts the full stack: the OtoDock server (dashboard included), PostgreSQL, document preview, and the supporting services. Your data lives in named Docker volumes (database, agent files, sessions), so stopping and restarting the stack keeps everything intact.

docker compose logs -f otodock-proxy # follow the server logs
docker compose down # stop the stack
Pinned releases

The compose file pins the OtoDock release it shipped with, so installs are reproducible. To run a different release, set OTODOCK_VERSION in .env — and see Upgrading & backups for the update flow. Release notes live on the GitHub Releases page.

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.

Put it behind HTTPS

For anything beyond a local trial, front port 8400 with a reverse proxy (Caddy, nginx, Traefik) or a tunnel that terminates TLS, and set DASHBOARD_PUBLIC_URL to the public https:// address. 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. (With DASHBOARD_PUBLIC_URL set, in-chat artifacts and mini-apps stay correctly sandboxed on that address even if a hop drops X-Forwarded-Proto.)

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