Skip to content

Configuration

Brandon Johnson edited this page Sep 30, 2026 · 5 revisions

Configuration

All configuration is via OBELO_* environment variables. There is no config file. Everything has a working default except the things that cannot have one (your TMDB key, your domain name).

This page is the complete reference — every variable the server reads. The authoritative source is internal/config/config.go, which documents each one at length.


Read this first: some variables only apply on the first boot

Three groups of settings — metadata providers, enrichment consent, and Tailnet remote access — are managed from the admin UI at runtime. Their environment variables exist so a headless deploy can come up already configured, and they seed the database on first boot only. After that the database is authoritative and the environment values are ignored.

This trips people up, so to be explicit: on an existing install, changing OBELO_TMDB_API_KEY or OBELO_TAILSCALE_HOSTNAME and restarting does nothing. The symptom is an operator restarting the server repeatedly wondering why the new value never takes. Change it in the web UI instead — where it also takes effect without a restart.

Every variable marked first boot below behaves this way. Everything else is read on every start.


Server and storage

Variable Default Purpose
OBELO_LISTEN_ADDR :8080 host:port the plain-HTTP listener binds to.
OBELO_DATA_DIR ./data (/data in the image) The single writable directory: SQLite DB, caches, transcode scratch, TLS material.
OBELO_SERVER_NAME derived The server's human-facing display name, shown in clients and advertised over mDNS. Empty derives one from the host's name. Purely cosmetic — nothing keys on it, and renaming never invalidates a device token.
OBELO_SCAN_INTERVAL 1h Scheduled incremental scan cadence. 0 disables scheduled scans (manual scans still work). Scans are incremental, so an unchanged library costs only a directory walk.
OBELO_SESSION_IDLE_TIMEOUT 90s How long an idle playback session lives before the reaper ends it. Clients report progress every ~10–15s, so this tolerates a network hiccup without killing an active stream. Not a login timeout.
OBELO_MAX_CONCURRENT_TRANSCODES 3 Cap on simultaneous transcodes. 0 is unlimited. Direct play and remux are unmetered — this bounds only the expensive tier.
OBELO_HARDWARE_ACCEL off off / auto / nvenc / vaapi / qsv / videotoolbox. A preference, validated at startup — see Hardware Transcoding.

LAN discovery

See LAN Discovery for what these fix and how to tell you need them.

Variable Default Purpose
OBELO_ADVERTISE_IP auto Comma-separated IPs to publish in the mDNS records. Set this when the boot log advertises a Docker bridge address instead of your LAN address.
OBELO_MDNS_INTERFACE auto Interface the mDNS responder listens on, e.g. eth0. Set this when the address is right but nothing discovers the server.

HTTPS and proxies

See HTTPS and Reverse Proxy. HTTPS is always an addition — the plain-HTTP listener keeps serving OBELO_LISTEN_ADDR.

Variable Default Purpose
OBELO_TLS_MODE off off / files / acme. Either non-off value adds an HTTPS listener.
OBELO_TLS_LISTEN_ADDR :8443 host:port for HTTPS. Must differ from OBELO_LISTEN_ADDR (the server refuses to start otherwise).
OBELO_TLS_CERT — Absolute path to the PEM certificate chain. Required in files mode.
OBELO_TLS_KEY — Absolute path to the PEM private key. Required in files mode.
OBELO_TLS_DOMAINS — Comma-separated public DNS names. Required in acme mode, and it is an exact list — no wildcards, and www. counts as a separate name.
OBELO_ACME_EMAIL — Optional contact address for the CA's expiry notices.
OBELO_ACME_DIRECTORY Let's Encrypt production ACME directory URL. Point it at https://acme-staging-v02.api.letsencrypt.org/directory while setting up.
OBELO_TRUSTED_PROXIES — (empty) CIDRs or bare addresses whose X-Forwarded-* headers are believed. Empty means none are, from anybody. Only list a proxy you control.

Remote access over a Tailnet — first boot only

Managed from Settings → Remote access in the web UI, which takes effect with no restart. These four seed it. See Remote Access with Tailscale.

Variable Default Purpose
OBELO_TAILSCALE_ENABLED false Join a tailnet at boot. Off means no node, no goroutine, no state directory, and zero contact with any coordination server.
OBELO_TAILSCALE_HOSTNAME derived The MagicDNS label to join under — a single DNS label. Empty derives one from the server name, falling back to obelo. Resolved once at seed time and then stored.
OBELO_TAILSCALE_CONTROL_URL Tailscale's Coordination server. Set it to your own Headscale if you would rather not depend on Tailscale's.
OBELO_TAILSCALE_HTTPS false Also serve HTTPS on the tailnet. Needs two prerequisites in the Tailscale console — and you want this on; see the page.
OBELO_TAILSCALE_AUTHKEY A pre-authorized join key for a headless first join, so nobody has to click a login link. It is consumed at join and never stored — deliberately kept out of the config struct so it cannot be persisted or echoed back by an API.

Requires the release build. The embedded node is compiled in only under -tags tailscale, which the Docker image and make build-release carry and a plain make build does not.

Linked servers

How often a linked server's libraries are pulled again. Everything else about a link — the credential, the addresses, the grants — lives in the database and is managed from Settings → Linked servers.

Variable Default Purpose
OBELO_LINK_SYNC_INTERVAL 1h Fallback refresh cadence for every link. The mirror is normally nudged the moment the sharing server's library changes, so this is the safety net. 0 turns the background half off entirely — no timer, no retry backoff, and no change subscription. Manual Sync now, and the pull that follows a link or a re-key, still work.

Metadata enrichment — first boot only

Enrichment is off until you turn it on: until enrichment consent is granted, Obelo makes zero outbound calls and every title simply shows as un-enriched. Official builds and the published Docker image carry default TMDB and fanart.tv keys, so once consent is granted they enrich Movies and TV with no key typed; your own key (BYOK) overrides the default. A build from source carries no default keys, so it enriches only what you configure. After first boot, manage all of this in the admin settings UI.

Variable Default Purpose
OBELO_TMDB_API_KEY bundled default (official builds); — from source Your own TMDB key for Movie and TV enrichment. Video enrichment is on exactly when a key is present — yours, or the bundled default.
OBELO_MUSICBRAINZ_ENABLED false Turns on Music enrichment. Needs no key.
OBELO_FANART_TV_API_KEY bundled default (official builds); — from source Your own fanart.tv key for artist and fan imagery.
OBELO_THEAUDIODB_API_KEY — Adds artist images and biographies from TheAudioDB.
OBELO_OPENSUBTITLES_API_KEY — Enables on-demand subtitle fetching.
OBELO_SUBTITLE_AUTO_FETCH_LANG — Language to auto-fetch subtitles in.
OBELO_METADATA_LANGUAGE en-US Preferred language/region tag for fetched metadata.
OBELO_AUTO_ENRICH true Enrich new titles immediately after a scan adds them.
OBELO_ENRICH_INTERVAL 6h Safety-net sweep that backfills anything still pending (e.g. added while a provider was down).
OBELO_ARTWORK_CANDIDATE_CACHE_TTL 2m How long a provider candidate-list result is cached, so toggling the artwork tab does not re-hit the provider.
OBELO_MUSICBRAINZ_RATE_LIMIT 1s Minimum interval between MusicBrainz requests, matching the public host's ~1 req/sec policy (it answers 503 above that). 0 disables throttling — only sensible against a mirror.
OBELO_ENRICHMENT_CONSENT unset Pre-answers the first-run consent prompt for a headless deploy. granted/true/1 grants, declined/false/0 declines, unset leaves it undecided — and an undecided server makes no outbound enrichment calls until someone answers.

Provider endpoint overrides

Rarely needed — they exist so tests can point enrichment at a local server, and so you can use a mirror.

Variable Default
OBELO_TMDB_BASE_URL https://api.themoviedb.org/3
OBELO_TMDB_IMAGE_BASE_URL https://image.tmdb.org/t/p/original
OBELO_MUSICBRAINZ_BASE_URL https://musicbrainz.org/ws/2
OBELO_COVERART_BASE_URL https://coverartarchive.org
OBELO_FANART_TV_BASE_URL https://webservice.fanart.tv/v3
OBELO_THEAUDIODB_BASE_URL https://www.theaudiodb.com/api/v1/json
OBELO_OPENSUBTITLES_BASE_URL the public OpenSubtitles API

Maintainer key rotation

An optional channel that lets official builds pick up replacement default provider keys without you updating anything.

A build from source never contacts it — the endpoint URL is injected at build time and is empty in source builds. Neither does a server whose operator has not granted enrichment consent. Between that and bringing your own keys, there are two independent routes to zero maintainer contact.

Variable Default Purpose
OBELO_KEY_ROTATION on Set to off (or false/0/no) to disable the channel entirely.
OBELO_KEY_ROTATION_URL build-injected; empty from source The endpoint polled. Empty disables the fetch as surely as turning the channel off.
OBELO_KEY_ROTATION_INTERVAL 6h Re-poll cadence after the first successful fetch. 0 means a single fetch at startup and no more.

Validation

Bad configuration is caught at startup with a message naming the variable, rather than failing mysteriously later. The server refuses to start if:

  • OBELO_TLS_MODE is not one of off, files, acme — a typo like fiels is not silently ignored, because that would leave you on plain HTTP believing you had TLS;
  • OBELO_TLS_LISTEN_ADDR equals OBELO_LISTEN_ADDR — HTTPS is an additional listener, not a replacement, so the two need separate ports;
  • files mode is missing a cert or key, or the pair does not match;
  • acme mode has no OBELO_TLS_DOMAINS, or a domain entry is malformed (including one carrying a port — a certificate is issued for a name, not a name:port);
  • an OBELO_TRUSTED_PROXIES entry is neither an address nor a CIDR.

The deliberate exception is acme mode failing to obtain a certificate, which does not stop the server: it boots, keeps serving plain HTTP, logs the reason, and keeps trying. A CA outage is not your typo, and it should not cost you your media server. See HTTPS for why the two behave differently.

Durations and lists

  • Durations are Go duration strings: 30m, 1h, 90s, 6h.
  • Lists are comma-separated with no spaces: media.example.com,www.example.com.
  • Booleans accept true/false, 1/0, and for the ones that read as switches, on/off.

Obelo

Home

Getting started

Reference

Going further

Plugins

Sharing with another household

Reaching it from away

When it breaks

Clone this wiki locally