Docker (Single-VM)
A self-hosted OpenTremor instance runs as a set of Docker containers on a single host — there is no separate installer package. The workspace root ships two Compose files; which one you want depends on what you’re running:
| File | Use for |
|---|---|
docker-compose.yaml | Local dev / demo — localhost, one HAProxy front door, path-routed. See Quick Start. |
docker-compose.prod.yaml | A real, internet-facing single-VM install — per-service subdomains, TLS via Let’s Encrypt, zero-downtime cert reload. Covered on this page. |
For a Kubernetes production deployment instead of a single host, see the
Helm chart in OpenTremor-deployments.
What docker-compose.prod.yaml runs
Section titled “What docker-compose.prod.yaml runs”All at the workspace root, alongside OpenTremor-core, both Terraform analyzer repos,
OpenTremor-dashboard, and OpenTremor-docs checked out as siblings:
mongodb— persistent storage (./data/mongodb)opentremor-core— API, built fromdocker/core-with-analyzers.Dockerfiledashboard— built domain-rooted (not path-prefixed), pointed at the API’s own subdomaindocs— static Starlight build served via nginxhaproxy— TLS termination and routing, the only container publishing80/443certbot/certbot-bootstrap/certbot-webroot/haproxy-reloader— request and renew Let’s Encrypt certificates and hot-reload HAProxy with zero dropped connections, with a temporary self-signed cert generated on first boot so HAProxy can bind:443before any real certificate exists
Each app service gets its own subdomain rather than being path-prefixed under one origin
(e.g. api.example.com, docs.example.com, bare example.com for the dashboard) — see
haproxy/prod.cfg.
Prerequisites
Section titled “Prerequisites”- A Linux host with Docker + Compose
- Three DNS records (API, docs, dashboard/bare domain) already pointing at the host’s public IP — Let’s Encrypt’s HTTP-01 challenge needs to reach the host over the real internet on port 80 before any cert can be issued
- Ports
80and443free on the host (HAProxy owns them)
Install
Section titled “Install”1. Configure
Section titled “1. Configure”Copy and edit OpenTremor-core/configs/opentremor-core-prod.yaml — see that file’s own
header comment for which fields have an environment-variable override. At minimum, set:
auth.api_key(bootstrap key — file-only, no env override)cors.allow_originsto your dashboard’s real origin (a credentialed request can’t use*)
Prefer environment variables (or an untracked env file) over editing secrets directly into
the YAML for anything that has an override — MONGODB_URI, JWT_SECRET,
LLM_CREDENTIAL_KEY, DEFAULT_ADMIN_EMAIL, DEFAULT_ADMIN_PASSWORD.
Since the dashboard and API don’t share an origin here, also set SERVER_COOKIE_DOMAIN
(e.g. .example.com) on the opentremor-core service in docker-compose.prod.yaml — without
it the session cookie set at login defaults host-only to the API’s subdomain, and the
dashboard’s server-side requests on the other host never see it. See
Dashboard Deployment
for the full explanation.
2. Bring up the stack
Section titled “2. Bring up the stack”docker compose -f docker-compose.prod.yaml up --build -dHAProxy binds :443 immediately using a temporary self-signed certificate — this is
expected before the next step.
3. Request real certificates (one-time)
Section titled “3. Request real certificates (one-time)”docker compose -f docker-compose.prod.yaml --profile init run --rm certbot-initEdit the --email in docker-compose.prod.yaml’s certbot-init service before running.
This is deliberately not part of the normal up — running it repeatedly during testing
risks Let’s Encrypt’s rate limits. Once it succeeds, haproxy-reloader picks up the real
certs on its next check (hourly), or force it immediately:
docker compose -f docker-compose.prod.yaml restart haproxy-reloader4. Set the dashboard base URL
Section titled “4. Set the dashboard base URL”Same one-time step as the quickstart, pointed at your real domain:
curl -X PATCH https://api.example.com/admin/settings \ -H "X-API-Key: <bootstrap-api-key>" -H "Content-Type: application/json" \ -d '{"server_dashboard_base_url": "https://example.com"}'Renewal
Section titled “Renewal”Unattended. The certbot service calls certbot renew roughly twice a day (a no-op unless
a certificate is within 30 days of expiry); haproxy-reloader notices the resulting file
change and reloads HAProxy with zero downtime. Nothing to schedule yourself.
Upgrade
Section titled “Upgrade”docker compose -f docker-compose.prod.yaml up --build -dRebuilds and replaces the app images; MongoDB data and certificates persist in their volumes/bind mounts untouched.
Backup
Section titled “Backup”Persistent state lives entirely in bind mounts under ./data/ — ./data/mongodb and
./data/certbot. Back those up; the containers themselves are stateless and disposable.