Self-hosting

Configuration

Every config.yaml field — database, security, timeouts and update settings.

Configuration lives in configs/config.yaml, generated on first run. You usually do not need to touch it.

server:
  port: 8080
database:
  driver: sqlite            # sqlite | postgres
  sqlite_path: ../data/yolorouter.db
  # host/port/user/password/dbname/sslmode are required when driver is postgres
log:
  level: info
security:
  provider_master_key: ""   # base64 AES-256 key; generated when left empty
  allow_private_upstreams: false  # allow loopback/private upstreams (local Ollama, vLLM, ...)
update:
  enabled: true             # set to false to disable the update-check API and CLI
  github_repo: ""           # "owner/repo" override for update checks
  github_proxy: ""          # e.g. https://gh.yolorouter.com/ to route updates via a mirror
gateway:                    # upstream forwarding timeouts; the whole block is optional
  connect_timeout: 5s       # TCP connect
  header_timeout: 600s      # request sent -> response headers
  first_byte_timeout: 600s  # headers -> first chunk (the "thinking" gap)
  body_idle_timeout: 60s    # maximum gap between two streaming chunks
  attempt_timeout: 20m      # hard cap for one key on one candidate
  request_timeout: 30m      # total budget across all failover candidates
  tls_handshake_timeout: 10s

Things worth knowing

  • A relative sqlite_path resolves against the config file's own directory, not the process cwd.
  • If the config file already exists, provider_master_key must be a real key — it is only auto-filled on the first-generation path.
  • A hand-copied config file must be chmod 600 or it will be refused.
  • allow_private_upstreams exists so you can point a provider at a local Ollama / vLLM / LM Studio. It is off by default as SSRF protection — never turn it on for an internet-exposed or multi-tenant deployment.
  • Timeout ordering is validated at startup: header_timeout and first_byte_timeout must be ≤ attempt_timeout, and attempt_timeout < request_timeout.

Why seven timeouts

A single wall clock kills reasoning models: a request that thinks for eight minutes before emitting a token is indistinguishable from a stalled upstream. Seven independent phases let you express both separately — first_byte_timeout tolerates long thinking, body_idle_timeout catches a stream that dies mid-flight, and request_timeout caps the whole thing.

Not everything is in this file. Input compression and custom system prompt injection are console settings stored in the database — see Cost optimization.

A fully annotated reference lives in the repository at configs/config.example.yaml.

CLI

Every subcommand accepts --config <path>.

./yolorouter serve            # start the HTTP service and background job manager
./yolorouter stop             # stop a running service
./yolorouter update           # self-update to the latest GitHub release
./yolorouter db:migrate       # apply pending migrations
./yolorouter db:status        # show the current migration version
./yolorouter db:rollback [v]  # roll back one migration, or down to version v
./yolorouter db:backup --output-dir backups
./yolorouter db:reset         # drop every table and re-migrate (dangerous); development
                              # builds only, disabled in release binaries
./yolorouter --version
./yolorouter --help