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.
cp .env.selfhost.example .env # then edit the 2 lines marked "EDIT ME"docker compose -f docker-compose.selfhost.yml up -dopen 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.
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 profiles.
Audience
Start here
Graduate to
Self-hoster, single user
this guide
—
Power user, CLI tools / web-cookie providers / sidecars
REQUIRE_API_KEY=true # was false — lock the endpoint downAPP_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:
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.
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.
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.
Add a provider and paste its API key. Keys are encrypted at rest with
AES-256-GCM; the cleartext never leaves your machine.
Point your IDE / agent at the unified entry:
http://127.0.0.1:20128/v1
For provider choice, see the
Free Tiers Guide — OmniRoute aggregates 150+ free
tiers into one endpoint, so you can run model: "auto" to pick the best free
option per request.
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).
.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:
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.
Prefer a binary over Docker? The npm package is the same code:
npm install -g omnirouteomniroute
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):
git clone https://github.com/diegosouzapw/OmniRoute.gitcd OmniRoutenpm installnpm run build && npm start
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:
Set REQUIRE_API_KEY=true in .env.
Read INITIAL_PASSWORD from the logs and log in.
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
and
Cloudflare Quick Tunnel
sections for copy-paste reverse-proxy configs.
You are running the fulldocker-compose.yml, whose app services are
profile-gated. For the one-command path use the self-host file:
docker compose -f docker-compose.selfhost.yml up -d
Or, with the full compose, pick a profile:
docker compose --profile base up -d.
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.
Raise OMNIROUTE_MEMORY_MB in .env (e.g. 4096), then
docker compose -f docker-compose.selfhost.yml up -d. See
Sizing the container.
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):