Repository navigation
Troubleshooting
Start with the boot log. Obelo states what it decided at startup — the advertised mDNS address, the resolved transcode backend, the TLS configuration, the tailnet state. Most questions on this page are answered by one line of it.
docker logs obelo # or: docker compose logs obelo
docker logs -f obelo # followI missed the first-admin claim token. If setup is not complete, restart the server — it is printed on every start while the database has no users. Once the first admin exists, setup permanently closes and the token stops being printed; from then on, users are created by an admin from the users screen.
The web app loads a blank page. You are running a binary built with plain
go build instead of make build. The React app is embedded into the binary
with go:embed, and the bundle has to be built first. Use make build (or
the Docker image).
I have to sign in again on a different address. Expected.
http://192.168.1.50:8080 and https://obelo.tail1a2b.ts.net are different
origins to a browser, and sessions do not cross origins. Native clients carry
their own token and are unaffected.
The scan finds nothing. Check the path is the one the server sees. In
Docker that is the container path (/media/movies), not the host path
(/srv/media/movies). This is the most common first-run mistake.
Files are found but identified wrongly. Identity comes from the file path
and embedded tags, not from a metadata provider — see
docs/naming-convention.md
for what the parser expects. You can also correct a match by hand, and the
correction survives later scans.
Titles vanished after a drive went offline. They are soft-deleted, not gone. Remount the drive and re-scan; the items and their watch state come back.
Nothing has artwork. Enrichment is off until you turn it on, and it needs both consent granted and at least one provider configured. With neither, Obelo makes zero outbound calls and every title shows un-enriched — which is working as designed, not a failure.
I set OBELO_TMDB_API_KEY and restarted, and nothing changed. Provider keys
seed the database on first boot only. On an existing install, set it in the
admin settings UI — where it also takes effect without a restart. See
Configuration.
Everything transcodes and the CPU is pegged. Check the admin Transcoding
tab for the resolved backend, then see
Hardware Transcoding. Note the boot log's validation
line — a configured backend that did not validate falls back to CPU libx264
with a warning, and keeps working.
The GPU block reads "unavailable". Expected without --gpus all, or on any
non-NVENC backend. Telemetry comes from nvidia-smi, which the NVIDIA Container
Toolkit injects. The rest of the tab still works.
The GPU works as root but not normally. The container user cannot open the
render node. Add the host's render GID:
--group-add "$(getent group render | cut -d: -f3)".
The fourth simultaneous stream is refused. OBELO_MAX_CONCURRENT_TRANSCODES
defaults to 3. Only full transcodes count against it; direct play and remux
are unmetered.
Playback stutters remotely but the LAN is fine. If you are on a tailnet, you have probably fallen back to a DERP relay — see Remote Access with Tailscale.
Clients cannot find the server. In Docker, mDNS needs --network host — it
is link-local multicast and does not cross a bridge. Then read the boot log's
advertising … line. Full flow on LAN Discovery.
dns-sd -B _obelo._tcp local finds nothing on the same Mac as the server.
A same-host artifact: macOS's mDNSResponder owns port 5353 and cannot see a
second responder on that host. Test from a different machine.
The server refuses to start after enabling TLS. Deliberate in files mode —
a missing, unreadable, or mismatched certificate is a typo you can fix in
seconds, and starting anyway would leave you on plain HTTP believing you had
TLS. The message names the wrong path. Also check that
OBELO_TLS_LISTEN_ADDR differs from OBELO_LISTEN_ADDR.
acme mode starts but there is no certificate. By design: a certificate
that cannot be obtained does not stop the server. It keeps serving plain HTTP,
logs the reason, and retries. Common causes, in order: DNS not pointed yet, the
port forward not set up, external port not exactly 443, CGNAT, or a rate
limit from earlier failed attempts.
Nothing happens at boot in acme mode. Correct — issuance is triggered by
the first HTTPS handshake for a listed name. Make that handshake from
cellular, not house wifi; many routers will not hairpin a connection to your own
public address.
Certificates are re-issued on every restart. OBELO_DATA_DIR/acme is not
durable. Mount a volume — otherwise you will hit the CA's duplicate-certificate
limit within days.
Rate-limited by Let's Encrypt. Production allows five failed authorizations
per hostname per hour. Switch to the staging directory
(OBELO_ACME_DIRECTORY=https://acme-staging-v02.api.letsencrypt.org/directory),
get it working there — the browser warning from the untrusted staging root is
the success signal — then switch back.
features.tailscale is false. You are running a development build. The
embedded node needs -tags tailscale, which the Docker image and
make build-release carry and a plain make build does not.
Remote access worked for months and then stopped. Tailscale node keys expire after 180 days by default. Disable key expiry for the node in the Tailscale console; nothing in Obelo can extend it.
The full list is on Remote Access with Tailscale.
A link is unreachable. Their machine is not answering. Nothing is
deleted — the libraries stay, badged unavailable, and their titles stay in
Continue Watching. Press Sync now; if it persists, ask them. On the Tailscale
machine-share path, a node key that expired after 180 days looks exactly like
this from your side, and there is no way to see it from here.
A link is revoked. They deleted the linked-server user or its device. Your
credential is dead and nothing retries, on purpose. Ask for a fresh invite and
press Re-key — the libraries, their local ids and your watch state are all
kept.
The invite would not redeem. Every refusal names the next move. "Expired", "already spent" and "unknown" are one message, and all three mean ask for a fresh one — including the case where the sharer pressed Generate invite a second time, which kills the first string.
No libraries arrived, but the link is connected. The sharer granted their linked-server user nothing. That is step 2 on their side; a linked-server user is created with no libraries deliberately.
"Stream limit reached" on one of their titles. Their server refused, not yours — the linked-server user is at its max streams cap and someone else in your household is already watching something of theirs.
Their 4K film plays at 1080p. Their playback ceiling, applied on their
machine. Your server never re-encodes anything of theirs; it only carries the
bytes, which is also why a relayed stream costs no slot against
OBELO_MAX_CONCURRENT_TRANSCODES.
I cannot scan, edit, or enrich a linked library. By design — the other server is the authority for its own files. Renaming it locally is the one exception.
I cannot share a library I received. Also by design, and not a bug to work around. Sharing does not travel.
See Linked Servers for all of it.
Everyone in the house shares one login-attempt budget. You are behind a
proxy Obelo has not been told about, so every request appears to come from the
proxy. Set OBELO_TRUSTED_PROXIES to the proxy's own address — see
Reverse Proxy.
The server refuses to start naming an OBELO_TRUSTED_PROXIES entry. The
entry is neither an IP address nor a CIDR prefix. Give the proxy's address, e.g.
127.0.0.1/32.
Back up OBELO_DATA_DIR/obelo.db. It holds your libraries, items,
hand-edits, users, and watch history.
The caches (artwork/, subtitles/) rebuild themselves and are not worth
backing up. acme/ is worth keeping and worth protecting — anyone who can
read it can impersonate your server to every client in the house.
Include the boot log, the build (GET /api/v1/server), how you are
running it (Docker flags or compose file, with secrets removed), and what you
expected instead. The boot log alone answers a surprising share of questions.
Getting started
Reference
Going further
Plugins
Sharing with another household
Reaching it from away
When it breaks