Self-hosting means running four long-lived Omnara services plus a one-shot migration job, pointed at your own Postgres, Redis, S3-compatible bucket, and persistent memory filesystem (Architecture), with one public URL in front of them.
The repository’s compose file runs everything behind the app profile:
git clone https://github.com/omnara-ai/omnara && cd omnaradocker compose --profile app up -d --build
That builds from source (a checkout auto-loads compose.override.yaml). To run published release images instead, use the base file alone, optionally pinning a release with OMNARA_RELEASE:
docker compose -f compose.yaml --profile app up -d
Open http://localhost:8000 and sign up — the local email driver is console, so the verification link will appear in docker compose logs api.
Run the release’s one-shot migrate image before starting its API, worker, and
maintenance services. Connect the migrator directly to the PostgreSQL writer;
do not use RDS Proxy or transaction-pooling PgBouncer. Long-running services
may use a compatible proxy. See database configuration
for connection-pool defaults.
Compose shares the persistent memory-data volume between API, worker, and maintenance at /var/lib/omnara/memory.Across hosts, mount the same shared filesystem into API, every worker, and maintenance and set OMNARA_MEMORY_DIR to its absolute mount path. Configure services to wait for the mount before starting, and verify shared locking and file visibility between hosts. See Memory storage configuration for filesystem requirements.Keep the mount private to API, workers, and maintenance. Back up the entire memory directory alongside PostgreSQL.
OMNARA_PUBLIC_URL is the public app origin used for email links, OAuth callbacks, and browser flows. The API defaults to the same deployment at /api/v1; set OMNARA_PUBLIC_API_URL when it is exposed at a different public URL. The daemon installer is served from the API origin at /install/omnarad.sh. These features will not function unless the configured URLs are publicly accessible.
Set OMNARA_PUBLIC_URL on every backend service. Outside local development it is required.
For a stable origin, add a domain to Cloudflare and create a named tunnel:
brew install cloudflared # or see Cloudflare's install docscloudflared tunnel logincloudflared tunnel create omnaracloudflared tunnel route dns omnara omnara.example.com # replace with your preferred domaincloudflared tunnel run --url http://localhost:8000 omnara
Then, restart the stack pointed at the tunnel’s URL:
OMNARA_PUBLIC_URL=https://omnara.example.com docker compose --profile app up -d
cloudflared service install will keep a named tunnel running across reboots. Any other reverse proxy or tunnel (Caddy, nginx, Tailscale Funnel, ngrok) works the same way, as long as it forwards the public Host header — the API rejects hosts it doesn’t recognize.
For a temporary tunnel without a separate domain, you can use:
cloudflared tunnel --url http://localhost:8000
This prints a random https://{subdomain}.trycloudflare.com URL that changes every run. After starting the tunnel you can set OMNARA_PUBLIC_URL in the same way described above.