Skip to content

Scheduling

Every watch has a cron schedule:

schedule: "30 3 * * *" # 03:30 every day

Watcher does not keep a timer per watch. Instead, something triggers a run, and each watch is asked: are you due?

A watch is due when:

  • it has never run — a new watch always runs on the next opportunity, or
  • the next scheduled time after its last run is now in the past.

A watch that is not due is skipped and recorded as skipped-not-due, costing nothing.

This is the rule that surprises people:

Effective frequency = the slower of your watch’s cron and how often the instance runs.

If the scheduled CI job runs hourly and your watch says */5 * * * *, your watch is checked hourly. Setting a faster cron on the watch cannot make the instance run more often.

Instance runs Watch cron Actual
hourly */5 * * * * hourly
hourly 0 * * * * hourly
hourly 30 3 * * * daily at the first run after 03:30 UTC
every 5 min (in-process scheduler) */5 * * * * every 5 minutes

To check more often, run the instance more often — not the watch.

If every source of a watch fails to collect, the run does not record a last-run time. The watch therefore remains due and is retried at the very next opportunity instead of waiting for its next cron slot. A transient portal outage costs you minutes, not a day.

Due-filtering can always be bypassed:

  • pnpm run-due:force from the CLI,
  • Run now in the panel or the app (POST /watches/:id/run),
  • Run all (POST /watches/run-all).

A manual run ignores the schedule entirely.

Watcher checks at points in time. If a page changes and changes back between two checks, Watcher never sees it; if your instance is down for a day, that day is not backfilled. For events that matter, prefer sources that carry history — a feed or an archive page — over a single volatile page.