--- title: "๐Ÿš€ Self-Host Guide โ€” OmniRoute (้›ถๆœˆ่ดน่‡ชๆ‰˜็ฎก / zero-fee self-host)" version: 3.8.51 lastUpdated: 2026-09-14 --- # ๐Ÿš€ Self-Host Guide โ€” OmniRoute > **TL;DR** โ€” three commands, one local endpoint, zero monthly fees. No SaaS > billing, no multi-tenant isolation, no hosted prompt-processing hop. Your > prompts go straight to the provider you pick. ```bash cp .env.selfhost.example .env # then edit the 2 lines marked "EDIT ME" docker compose -f docker-compose.selfhost.yml up -d open http://127.0.0.1:20128 ``` This is the **self-host carrier** for OmniRoute's "้›ถๆœˆ่ดน + ่‡ชๆ‰˜็ฎก" product form: a packaged container/binary you run on your own machine in 5 minutes. --- ## Why this guide exists OmniRoute ships a sophisticated `docker-compose.yml` with profiles (`base`, `web`, `cli`, `host`, `cliproxyapi`, `memory`, `bifrost`). Each app service is profile-gated, so a bare `docker compose up -d` only starts Redis. That is correct for power users who pick a profile โ€” but it is *not* a 5-minute self-host story. `docker-compose.selfhost.yml` is the KISS overlay: **one command, published image, loopback-only, Redis included, no profile choice, no build step.** When you outgrow it, graduate to the full [DOCKER_GUIDE](../guides/DOCKER_GUIDE.md) profiles. | Audience | Start here | Graduate to | | --- | --- | --- | | Self-hoster, single user | this guide | โ€” | | Power user, CLI tools / web-cookie providers / sidecars | โ€” | `docker-compose.yml` profiles | --- ## Prerequisites - Docker Engine 24+ (or Docker Desktop 4.30+) with the Compose v2 plugin. - ~2 GB RAM free (see [Sizing](#sizing-the-container)). - A provider API key from any supported provider (OpenAI, Anthropic, Google, or one of the [150+ free tiers](./FREE-TIERS-GUIDE.md)). No build toolchain, no Node, no git clone required โ€” the image is pulled. --- ## Step 1 โ€” Configure (1 min) ```bash cp .env.selfhost.example .env ``` Edit exactly **two** lines in `.env`: ```dotenv REQUIRE_API_KEY=true # was false โ€” lock the endpoint down APP_BIND_HOST=127.0.0.1 # keep loopback; see "Exposing" only if needed ``` `REQUIRE_API_KEY=true` makes every `/v1` request and the dashboard require an API key / login. On first boot the dashboard auto-generates an `INITIAL_PASSWORD` โ€” read it from the logs: ```bash docker logs omniroute | grep -i password ``` The other variables (`DASHBOARD_PORT`, `API_PORT`, `LIVE_WS_PORT`, `OMNIROUTE_MEMORY_MB`) already have sane defaults. Leave them unless you know you need to change them. --- ## Step 2 โ€” Start (1 min) ```bash docker compose -f docker-compose.selfhost.yml up -d ``` Pulls `diegosouzapw/omniroute:latest` (multi-arch AMD64 + ARM64, ~250 MB) and `redis:8.6.5-alpine`, starts both, and waits for Redis to be healthy before the app boots. ### Step 3 โ€” Verify (30 s) ```bash # process lifecycle + readiness curl -fsS http://127.0.0.1:20128/healthz && echo # container health docker inspect --format '{{.State.Health.Status}}' omniroute ``` You should see `{"status":"ok"}` and `healthy`. Then open the dashboard: ``` http://127.0.0.1:20128 ``` Within ~300 s of `up -d` the endpoint is locally reachable and the healthcheck is `healthy` โ€” the acceptance bar from the self-host issue. --- ## Ports | Port | What | Default bind | | --- | --- | --- | | `20128` | Dashboard + `/v1` LLM proxy (unified entry) | `127.0.0.1` | | `20129` | API port (server-to-server) | `127.0.0.1` | | `20132` | Live WebSocket (realtime dashboard updates) | `127.0.0.1` | All three bind to **loopback only** by default. Redis is **not** published to the host at all โ€” the app reaches it over the compose network. This is deliberate: shipping an unauthenticated Redis on `0.0.0.0` is a footgun. --- ## Connecting a provider 1. Open the dashboard โ†’ **Providers**. 2. Add a provider and paste its API key. Keys are encrypted at rest with AES-256-GCM; the cleartext never leaves your machine. 3. Point your IDE / agent at the unified entry: ``` http://127.0.0.1:20128/v1 ``` For provider choice, see the [Free Tiers Guide](./FREE-TIERS-GUIDE.md) โ€” OmniRoute aggregates 150+ free tiers into one endpoint, so you can run `model: "auto"` to pick the best free option per request. --- ## Sizing the container The image pins `OMNIROUTE_MEMORY_MB=1024`. That is enough for the dashboard and light chat. **Coding agents** (`POST /v1/responses` from Claude Code, Codex, Grok, โ€ฆ) retain multiple large context graphs during compression and can abort V8 at ~12 GiB old-space under two overlapping long contexts ([#7849](https://github.com/diegosouzapw/OmniRoute/issues/7849)). `.env.selfhost.example` defaults to `OMNIROUTE_MEMORY_MB=2048` โ€” a safe floor for a single user running coding agents. Raise it if you fan out many models in parallel (fusion combos) or hit `FATAL ERROR: Reached heap limit`: ```dotenv OMNIROUTE_MEMORY_MB=4096 ``` Memory is a V8 heap ceiling; native buffers (SQLite, ONNX, better-sqlite3) sit outside it, so size the container a few hundred MB above the heap. --- ## Local binary build (optional) Prefer a binary over Docker? The npm package is the same code: ```bash npm install -g omniroute omniroute ``` This runs the Next.js standalone server directly on your host โ€” same ports, same `DATA_DIR` (`./data` by default). Use it when you cannot run Docker (e.g. a locked-down VM). The container path above is the recommended default because it bundles the exact runtime the image was tested with. From source (development only โ€” not a deploy path): ```bash git clone https://github.com/diegosouzapw/OmniRoute.git cd OmniRoute npm install npm run build && npm start ``` --- ## Exposing beyond localhost **Do not** flip `APP_BIND_HOST=0.0.0.0` while `REQUIRE_API_KEY=false`. That publishes an open `/v1` proxy on every LAN/WAN interface โ€” anyone on the network can burn your provider quotas. The order is fixed: 1. Set `REQUIRE_API_KEY=true` in `.env`. 2. Read `INITIAL_PASSWORD` from the logs and log in. 3. *Only then* set `APP_BIND_HOST=0.0.0.0` (or put an auth-enforcing reverse proxy in front and keep loopback). For TLS / a domain, run Caddy or Traefik in front and leave `APP_BIND_HOST` at `127.0.0.1`. See the [DOCKER_GUIDE โ€” Caddy HTTPS](../guides/DOCKER_GUIDE.md#docker-compose-with-caddy-https-auto-tls) and [Cloudflare Quick Tunnel](../guides/DOCKER_GUIDE.md#cloudflare-quick-tunnel) sections for copy-paste reverse-proxy configs. --- ## Data, backups, and resets - **Data dir**: `./data` (bind-mounted to `/app/data`). All SQLite DBs, migrations, audit trail, and encrypted provider keys live here. - **Backups**: SQLite auto-backup is on by default (`DISABLE_SQLITE_AUTO_BACKUP` is unset โ†’ enabled). For a manual snapshot: ```bash ./bin/snapshot-data.sh ``` Restore with `./bin/restore-data.sh`. - **Reset the dashboard password**: ```bash docker exec -it omniroute node bin/reset-password.mjs ``` - **Reset policies** (routing/failover/quota to factory defaults): ```bash docker exec -it omniroute node bin/restore-policies.sh ``` --- ## Common issues
docker compose up only starts Redis You are running the **full** `docker-compose.yml`, whose app services are profile-gated. For the one-command path use the self-host file: ```bash docker compose -f docker-compose.selfhost.yml up -d ``` Or, with the full compose, pick a profile: `docker compose --profile base up -d`.
Healthcheck stays starting / unhealthy - Check Redis is up: `docker inspect --format '{{.State.Health.Status}}' omniroute-redis` - Check app logs: `docker logs omniroute` - The healthcheck probes `/healthz` and allows a 20 s start period. A slow first boot (cold migrations) can take longer โ€” bump `start_period` in the compose file if your disk is slow.
FATAL ERROR: Reached heap limit under coding agents Raise `OMNIROUTE_MEMORY_MB` in `.env` (e.g. `4096`), then `docker compose -f docker-compose.selfhost.yml up -d`. See [Sizing the container](#sizing-the-container).
Web-cookie providers (Gemini Web, Claude Turnstile) fail with Executable doesn't exist at .../ms-playwright/chromium The `base` image ships without Chromium. The self-host compose uses the published `base` image. For web-cookie providers, switch to the full compose with the `web` profile (which bundles Playwright/Chromium): ```bash docker compose --profile web up -d ``` See [DOCKER_GUIDE โ€” Available Profiles](../guides/DOCKER_GUIDE.md#available-profiles).
Port 20128 already in use Set `DASHBOARD_PORT`, `API_PORT`, `LIVE_WS_PORT` in `.env` to free ports and re-run `up -d`.
--- ## What this deliberately is NOT Per the self-host KISS constraint, this path **does not** include: - โŒ SaaS billing / metering / plan tiers - โŒ Multi-tenant isolation / per-tenant namespaces - โŒ A hosted prompt-processing hop (your prompts go straight to the provider) - โŒ Any baked-in credentials or secrets It is a **pure local self-host** carrier โ€” the simplest thing that makes the "้›ถๆœˆ่ดน + ่‡ชๆ‰˜็ฎก" promise real. --- ## Security checklist (self-host) Before you expose beyond loopback: - [ ] `REQUIRE_API_KEY=true` in `.env` - [ ] `INITIAL_PASSWORD` rotated to a strong, unique value - [ ] `APP_BIND_HOST` left at `127.0.0.1` *unless* behind an auth-enforcing proxy - [ ] TLS terminated by Caddy/Traefik/Cloudflare in front (never plain HTTP on WAN) - [ ] Redis not published to the host (the self-host compose already enforces this) - [ ] `./data` volume backed up regularly (`bin/snapshot-data.sh`) - [ ] Provider keys rotated per the provider's own policy For the full supply-chain / image / dependency audit dimension, see [SECURITY.md](../../SECURITY.md) and [SUPPLY_CHAIN](../security/SUPPLY_CHAIN.md). --- ## Related - [Docker Guide](../guides/DOCKER_GUIDE.md) โ€” profiles, Caddy HTTPS, tunnels, image tags - [Quick Start](./QUICK-START.md) โ€” 3-minute path for first-time users - [Free Tiers Guide](./FREE-TIERS-GUIDE.md) โ€” 150+ free provider tiers - [Providers Guide](./PROVIDERS-GUIDE.md) โ€” connecting and configuring providers - [Troubleshooting](../guides/TROUBLESHOOTING.md) โ€” deeper issue resolution