OVTH / 2026 LIVE · V0.4.0
Ø Overthinking Gateway ↗

Drop OpenRouter: self-host the OVTH Gateway

A migration guide from OpenRouter to a self-hosted Gateway. Same OpenAI-compatible API, same models, half the latency, none of the markup.

Updated May 08, 2026 by xlrd · Fig. S03

Who this is for

You burn more than $50/mo on OpenRouter and have noticed that 5% markup is starting to show on the invoice. You already have keys for the underlying labs (Anthropic, OpenAI, Gemini). You are okay running a small Docker container on a box you own. You want one endpoint for every model and you want it to be yours.

If you are under $50/mo, stay on OpenRouter — the engineering time to migrate is not worth the arbitrage.

The migration, one diff

diff
- OPENAI_API_BASE=https://openrouter.ai/api/v1
- OPENAI_API_KEY=sk-or-v1-...
+ OPENAI_API_BASE=https://gateway.your-domain.dev/v1
+ OPENAI_API_KEY=ovth_...

That is the whole change for 90% of clients. SDKs, CLIs, IDEs — Cursor, Aider, OpenCode, Continue, Claude Code (via ANTHROPIC_BASE_URL) — all speak this shape.

Tools and versions

  • A VPS with 1GB RAM, anywhere (Hetzner CX11 is €4/mo, we run one)
  • Docker 24+ and docker compose
  • Caddy or any reverse proxy that terminates TLS
  • OVTH Gateway 0.9.x (Docker image: ghcr.io/nousresearch/ovth-gateway)
  • Your existing provider keys

Setup in five steps

01. Provision the box

bash
# Hetzner or your VPS of choice
ssh root@your-box
curl -fsSL https://get.docker.com | sh
mkdir -p /srv/ovth && cd /srv/ovth

02. Compose the Gateway

yaml
# /srv/ovth/docker-compose.yml
services:
gateway:
  image: ghcr.io/nousresearch/ovth-gateway:0.9
  restart: always
  ports: ["127.0.0.1:8080:8080"]
  env_file: .env
  volumes:
    - ./data:/data
    - ./config.yaml:/app/config.yaml:ro
yaml
# /srv/ovth/config.yaml
routes:
auto:
  strategy: cheapest-capable
  providers: [anthropic, openai, google]
cheap:
  providers: [google, anthropic]
  models: [gemini-1.5-flash, claude-haiku-4]
smart:
  providers: [anthropic, openai]
  models: [claude-opus-4.7, gpt-5]
quota:
daily_tokens: 500000
per_user: true
bash
# /srv/ovth/.env
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-...
GOOGLE_API_KEY=...
OVTH_MASTER_KEY=$(openssl rand -hex 32)

03. Terminate TLS with Caddy

caddyfile
# /etc/caddy/Caddyfile
gateway.your-domain.dev {
reverse_proxy 127.0.0.1:8080
encode gzip zstd
header {
  Strict-Transport-Security "max-age=31536000"
  X-Frame-Options "DENY"
}
}
bash
docker compose up -d
systemctl reload caddy
curl https://gateway.your-domain.dev/v1/models | jq '.data | length'
# → 28 or so

04. Mint a per-app key and flip your clients

bash
curl -X POST https://gateway.your-domain.dev/admin/keys \
-H "Authorization: Bearer $OVTH_MASTER_KEY" \
-d '{"label":"cursor","quota":{"daily_tokens":100000}}'
# → {"key":"ovth_live_..."}

Rotate this key whenever. One Gateway, many apps, independent quotas.

05. Verify with a drop-in curl against both APIs

bash
# OpenAI shape
curl https://gateway.your-domain.dev/v1/chat/completions \
-H "Authorization: Bearer ovth_live_..." \
-d '{"model":"auto","messages":[{"role":"user","content":"hi"}]}'

# Anthropic shape — same gateway, different path
curl https://gateway.your-domain.dev/anthropic/v1/messages \
-H "x-api-key: ovth_live_..." \
-H "anthropic-version: 2023-06-01" \
-d '{"model":"claude-sonnet-4.5","max_tokens":50,"messages":[{"role":"user","content":"hi"}]}'

Both should return a real model response. If they do, you are done. Flip the env vars in your clients.

Cost, privacy, performance

Self-hosting is the second derivative of control. The first was moving off ChatGPT Pro. This one is moving off the thing you moved onto. Own the endpoint, own the invoice, own the logs.