Your job didn't fail. It just stopped writing.

Stalewatch learns when each Postgres table normally gets new rows. You'll get a Slack alert when a table goes quiet at an hour it is usually busy. No thresholds to tune, nothing to add to your code.

For backend engineers whose cron jobs, queue workers and webhooks write to Postgres, and data engineers whose syncs feed it. The failure it is for: nothing errors, nothing pages, and a table just stops getting rows.

Status: Early access opens to the waitlist in signup order.

See it work Hold a founding spot

Input

Text14 days of write activity
public.orders (1 of 3 tables)
hour      0     6     12    18
Wed 10-21 ........############....
Thu 10-22 ........############....
Fri 10-23 ........############....
Sat 10-24 ........................
Sun 10-25 ........................
Mon 10-26 ........############....
Tue 10-27 ........############....
Wed 10-28 ........############....
Thu 10-29 ........############....
Fri 10-30 ........############....
Sat 10-31 ........................
Sun 11-01 ........................
Mon 11-02 ........############....
Tue 11-03 ........######..##

# got rows that hour, . got none.

Output

2 alerts instead of 23

Across 3 tables and the last 14 days: a fixed 60-minute threshold would have sent 23 alerts, 21 of them at hours these tables are normally quiet. Learned rhythm: 2 alerts.

  1. 1 alert public.orders

    Went quiet Tue 2026-11-03 14:00–16:00. Normally gets rows: Weekdays 08:00–20:00; weekends no regular hours.

  2. 1 alert public.daily_rollup

    Went quiet Mon 2026-11-02 02:00–03:00. Normally gets rows: Weekdays 02:00–03:00; weekends 02:00–03:00.

  3. No alerts public.events

    Normally gets rows: Weekdays every hour; weekends every hour.

The Slack message for the latest alert:

public.orders got no new rows from 14:00 to 16:00 on Tue 2026-11-03. It normally gets rows on weekdays 08:00–20:00.

What Stalewatch did

  • Stalewatch: Learned each table's normal hours from 14 days of timestamps

    By hand: pick a threshold per table, and re-tune it when the schedule changes

  • Stalewatch: Replayed the 14 days to separate real outages from normal quiet

    By hand: a fixed threshold alerts every night and weekend, so people mute it

  • Stalewatch: Wrote the Slack message with the table, the gap and its normal hours

    By hand: write and maintain the alert yourself

See the full sample

Hold a founding spot

The first 100 on the waitlist get $15/mo, locked for as long as you stay subscribed. Then it's $29/mo.

We store your email, a source tag, your consent and the signup time in Cloudflare D1, to email you about Stalewatch. Privacy

BVL privacy policy

Why not a cron script or dbt freshness?

A cron script you or an AI assistant write
Easy to write. You pick a fixed threshold per table, host it, and notice when it breaks. When it stops running, it fails as quietly as the job it watches.
dbt source freshness
Free if you use dbt. It uses fixed warn and error thresholds per source, so a table that is quiet every night and weekend needs hand-tuning.
A heartbeat monitor (cron pings)
Tells you a job ran, not that it wrote rows, and needs a ping added to every job.
Stalewatch
Learns each table's normal hours from the rows themselves. It'll run outside your stack and tell you if it cannot connect.

How it works

  1. You'll create a Postgres role that can read one timestamp column per table, for example GRANT SELECT (created_at) ON public.orders TO stalewatch.
  2. Stalewatch learns each table's rhythm from the timestamps already in it, so there is no warm-up: which hours on weekdays and weekends normally get rows.
  3. When a normally busy hour gets no rows, you'll get one Slack message, and another when rows come back. If Stalewatch cannot connect, it'll tell you that too.

Price

$15/mo for the first 100 on the waitlist, then $29/mo. Nothing is charged when you join.