Owly Post
Self-hosted

Scheduling

The three schedules, how to set the digest slot, and what happens when a run is missed

The worker container runs everything on a schedule; the app container only runs what you trigger by hand. How a digest is assembled is on Digest and delivery.

The three schedules

JobWhat it doesSet whereDefault
IngestFetches every source and summarises new itemsINGEST_CRON in .env0 */6 * * * (every 6 h)
CleanupClears old article text (retention)CLEANUP_CRON in .env15 4 * * * (daily 04:15)
DigestFetches once more, then writes and sends the digestIn the app, under SettingsWeekly, Sunday 17:00

All three are evaluated in DIGEST_TIMEZONE (default UTC). Set it to your own zone, or the digest lands at a surprising hour.

Set the digest slot

  1. Open Settings and find Digest delivery.
  2. Choose Weekly or Daily. For weekly, pick the weekday.
  3. Set the time and press Save schedule.

The worker re-reads the schedule every minute, so the change takes effect within a minute without a restart. The worker logs the new slot, for example Digest scheduled weekly on Monday at 08:00 ("0 8 * * 1", Europe/Amsterdam).

Change ingestion or cleanup

These two are environment variables. Edit .env, then restart the worker:

Terminal
docker compose up -d worker

An invalid cron expression stops the worker at startup with Invalid INGEST_CRON cron expression (or CLEANUP_CRON); check docker compose logs worker.

What happens with a missed run

The worker keeps no record of when a job last ran. A scheduled tick that fires up to 10 minutes late still runs; anything later is skipped until the next scheduled slot. There is no catch-up.

In practice:

  • The host was asleep or off at the digest slot (a laptop on Sunday at 17:00): no digest that week. Press Generate digest now on the Digests page, or move the digest to a moment the machine is reliably on.
  • The worker was restarted after the slot had passed: the same; the next occurrence runs normally.
  • A missed ingest tick is harmless: every digest run fetches all sources first, so the digest always sees what was published since the last successful fetch.

Run it on an always-on box

A Raspberry Pi, a NAS or a small VPS that never sleeps makes this a non-issue. The image is built for arm64 as well as amd64.

Manual runs and scheduled runs

Fetch now and Generate digest now call the same jobs as the worker, but they run inside the app container and are fire-and-forget: the button immediately says Started in the background; refresh in a bit., whether or not the job later succeeds. The only evidence of a failure is docker compose logs app or a failed digest on the Digests page.

Nothing locks a manual run against a scheduled one, since they run in different containers. That is safe in practice: a completed digest marks its items as digested, so a second run started at the same time finds nothing left and records an empty digest.

On this page