Troubleshooting
From symptom to cause to fix, starting with the four most common ones
Start with the logs. Almost every problem below leaves a line there.
docker compose logs --tail=200 worker
docker compose logs --tail=200 appNo digest arrived
Cause
- The slot was missed. A digest tick that fires more than 10 minutes late is skipped, with no catch-up. The host was asleep or off, or the worker was restarting. See Scheduling.
DIGEST_TIMEZONEis unset, so the slot is evaluated in UTC and lands at another hour than you expect.- The run failed. The Digests page shows a digest with status
failed; open it and expand Raw model response or error. - There was nothing new. The Digests page shows an
emptydigest, and no email is sent for those by design. - The digest was generated but not mailed: status
ready. See Email does not arrive.
Fix
- Press Generate digest now on the Digests page; check
docker compose logs appforDigest run finishedor an error. - Set
DIGEST_TIMEZONE=Europe/Amsterdam(your zone) in.envanddocker compose up -d worker. - For a
faileddigest on a local model, see Digest failed on Ollama below; on an API provider, check the key and the account's credits.
A feed is not being fetched
Cause
- The source is in status
error: five consecutive failures. Its last error is shown under the source on the Sources page and on the dashboard under Sources in error. Errored sources are retried once a day. Feed responded with HTTP 403or404: the site blocks bots, or the feed moved.Blocked private address for …: the feed resolves to a private or local network address, which the fetch guard refuses by design. Feeds on your LAN cannot be followed.- Nothing is wrong: the feed answered
304 Not Modified, which is the normal case for a feed without new posts.
Fix
- Press Resume on the source to reset the failure count and retry immediately.
- Check the URL in a browser; if the feed moved, delete the source and add the new URL.
- Read the exact message:
docker compose logs worker | grep "Ingest failed".
The summary is empty
Cause
- Not written yet. Summaries are produced in batches of 100 per fetch run, so a large backlog takes a few runs.
- The model call failed for that item:
Summary failed for item …in the logs. The item is retried on the next run. - The provider rejects every call: wrong key, no credits (Anthropic needs API credits, not a subscription), or a local server that is not reachable from the container.
- The item genuinely has no content: a feed that only carries a title and a link, and a page the extractor could not read.
Fix
- Wait for the next ingest run, or press Fetch now.
docker compose logs app worker | grep "Summary failed"and read the reason.- For a local server, confirm the container can reach it:
docker compose exec worker node -e "fetch('http://host.docker.internal:11434/v1/models').then(r => console.log(r.status))"should print200.
Email does not arrive
Cause
- Email is not fully configured, so it is silently disabled. Resend needs
RESEND_API_KEY; SMTP needsSMTP_HOST; both needDIGEST_EMAIL_FROMandDIGEST_EMAIL_TO. Email skipped: SITE_URL is not configured.: the mail needsSITE_URLto build its links.Resend error: …: usually the From address is not on a domain verified in Resend.- SMTP TLS mismatch: port 465 needs
SMTP_SECURE=true; port 587 starts plain and upgrades with STARTTLS. - A relay on the Docker host is configured as
localhost, which inside the container is the container itself. Usehost.docker.internal(on Linux, add theextra_hostsmapping to the worker service). - The send failed after generation: the digest stays
ready, the run does not fail, and the error is only in the logs.
Fix
docker compose logs worker | grep -i emailshows exactly which of the above it is.- Fix
.env,docker compose up -d, and press Generate digest now to test. A digest with statussentconfirms delivery.
Less common
Requests to the API are answered with 403 {"error":"Invalid host."}. You are reaching the app by
a hostname or LAN address that is not on the Host allowlist; pages still render because only the
API is guarded.
Fix: set SITE_URL=https://owly.example.com or ALLOWED_HOSTS=owly.example.com,192.168.1.10
in .env and docker compose up -d app. localhost and 127.0.0.1 are always allowed.
docker compose up aborts with set POSTGRES_PASSWORD in .env.
Fix: the bundled Postgres never starts with an empty password. Generate one with
openssl rand -base64 24 and set POSTGRES_PASSWORD in .env.
docker compose logs migrate shows Migration 0002_… failed: categories has case-insensitive duplicate names, or Migration 0003_… failed mentioning btree_gist.
Fix: for 0002, rename one of the two categories in the app and run docker compose up -d
again. For 0003 on a managed Postgres, enable the btree_gist extension from the provider's
dashboard first. See Updating.
The digest shows Model did not return valid JSON after retry. The digest call needs one valid
JSON document covering every item, and small local models drift into prose or cut the answer
short.
Fix: set LLM_MODEL_DIGEST to a bigger model (qwen3:14b or larger) and try
Generate digest now again. Summaries can stay on the small model. See
LLM providers.
The manual buttons are fire-and-forget: they always report Started in the background; refresh in a bit., even when the job then fails.
Fix: docker compose logs app shows the actual run: Ingest run finished,
Digest run finished, or the error that stopped it.