Skip to content

Configuration

Iain Smith edited this page Sep 2, 2026 · 2 revisions

Configuration

Every setting is an environment variable with the prefix TVTIMES_. The full annotated list is .env.example; this page covers the ones you actually touch.

Essential

Variable Notes
TVTIMES_PUBLIC_ORIGIN The exact URL the browser uses — scheme, host, port, no trailing slash. Passkeys are bound to it. http://192.168.x.x:8888 is fine on a trusted LAN; use https://… behind a reverse proxy.
TVTIMES_WEBAUTHN_RP_ID The registrable domain of that origin (no scheme, no port); localhost for a bare-IP setup. Changing it later invalidates every passkey.
TVTIMES_HTTP_PORT Host port to expose (default 8888).
POSTGRES_PASSWORD Used by the db container and the app's connection string.
TVTIMES_EMAIL_PROVIDER console (log only) · smtp (+ TVTIMES_SMTP_*) · resend (+ TVTIMES_RESEND_API_KEY). See Email.

Hard requirements in env=prod

The API refuses to start without these — but the container entrypoint satisfies the two secrets for you and reuses them forever:

  • TVTIMES_ENV=prod
  • TVTIMES_JWT_PRIVATE_KEY_PEM — Ed25519 private key, PKCS#8 PEM
  • TVTIMES_ENCRYPTION_KEY — 32 urlsafe-base64 bytes (a Fernet key)
  • TVTIMES_DATABASE_URLpostgres://, postgresql:// and postgresql+asyncpg:// all accepted
  • TVTIMES_REDIS_URL

If TVTIMES_ENCRYPTION_KEY or TVTIMES_JWT_PRIVATE_KEY_PEM ever change, every stored source credential and TOTP secret becomes unreadable and all sessions drop. Let the entrypoint manage them (persist /data), or set real values and keep them.

Behind a reverse proxy

  • TVTIMES_TRUSTED_PROXIES — comma-separated proxy IPs / CIDRs whose X-Forwarded-For the app will trust for the client IP (rate limiter, audit log). Empty by default, meaning every request is treated as direct — set it whenever a proxy is in front. A same-host proxy is 127.0.0.1; the compose network is 172.16.0.0/12. See Reverse Proxy and HTTPS.

Behind multiple API replicas

  • TVTIMES_RATELIMIT_STORAGE_URI=redis://… — share Redis so rate limits hold across replicas (memory:// is per-process).
  • The web container is stateless; scale it horizontally behind a load balancer. Run exactly one worker.

Outbound-fetch allowlist

User-supplied URLs (M3U / Xtream / Stalker / standalone XMLTV) are fetched through an SSRF guard that rejects private, loopback, link-local and CGNAT addresses. To point tvtimes at a service on your own network (a Pluto proxy, xTeVe/Threadfin, another box):

TVTIMES_FETCH_ALLOWLIST=192.168.0.218        # host, or a CIDR: 192.168.0.0/24

then docker compose up -d --force-recreate. Native HDHomeRun sources are exempt — they require a private LAN address.

TVTIMES_FETCH_MAX_BYTES (default 256 MiB) caps both a download and a gzip feed's decompressed size, so a compression bomb can't exhaust memory.

Health endpoints

  • GET /api/healthz — liveness, returns the version (used by the compose healthcheck)
  • GET /api/readyz — readiness, checks the database

Clone this wiki locally