How to monitor Supabase backups with Healthchecks.io
A backup job that fails with an error is the easy case. The one that hurts is the job that stopped running months ago, which nobody notices until they need a restore. Healthchecks.io handles both: it expects to hear from your job on a schedule, and it alerts you when a run fails or doesn’t report in. From version 2.5.0, the open-source backupdrill CLI sends those reports itself, for backups and for restore drills. Setup is one check and one environment variable.
The short version
- Needs
- backupdrill CLI 2.5.0 or newer, for backup and drill
- Setting
- BACKUPDRILL_PING_URL, the --ping-url flag, or pingUrl in backupdrill.config.json
- Pings
- <url>/start when a run begins, then <url> on success or <url>/fail on failure, both with the same run ID
- If a ping fails
- Each attempt times out after 10 seconds; network and server errors get up to two retries, then a warning. The backup's result and exit code don't change
- Healthchecks.io free plan
- Hobbyist, $0/month, 20 checks
Checked October 19, 2026.
Where scheduled backups go quiet
A backup can stop running, or keep failing where nobody looks. Four ways it happens:
- GitHub switches off the schedule. In a public repository, scheduled workflows are disabled after 60 days without repository activity. Nothing fails; the runs just stop appearing. The GitHub Actions backup guide has the details.
- The machine isn’t on. A cron job on a laptop or a small server doesn’t run while it sleeps, reboots or sits with a full disk.
- Credentials change. Someone resets the database password or rotates a bucket key, and every run after that exits with an error into a log nobody opens.
- The project is paused. Supabase pauses a Free plan project after a week of low activity, and backups fail until someone resumes it.
A check on Healthchecks.io works the other way round. It doesn’t wait for an error. It expects a ping every day (or week), and silence is enough to raise the alert.
Set it up
This assumes you already run backups with the open-source backupdrill CLI (MIT, github.com/backupdrill/cli) from cron or GitHub Actions. If not, start with the CLI docs. Run backupdrill --version first: versions before 2.5.0 ignore BACKUPDRILL_PING_URL and pingUrl without a warning, and reject --ping-url as an unknown option.
Step 1 — create a check
Sign up at Healthchecks.io (the free Hobbyist plan covers 20 checks) or use your own self-hosted Healthchecks, and add a check. Give it the same schedule as the backup job: a Simple schedule with a Period of 1 day or 1 week, or a Cron schedule with the job’s exact cron expression and time zone.
Then set Grace Time, and make it generous. Because the CLI sends a start ping, Healthchecks also uses Grace Time as the longest a run may take between start and finish. Take your slowest backup so far, add a margin, and add more on GitHub Actions, where scheduled runs can start late.
Copy the check’s ping URL. It looks like https://hc-ping.com/<uuid>. Keep it secret: anyone who has it can tell Healthchecks your backup ran.
Step 2 — give the ping URL to the CLI
The CLI reads it from the BACKUPDRILL_PING_URL environment variable, the --ping-url flag, or a pingUrl key in backupdrill.config.json. When more than one is set, the flag wins, then the variable, then the file, the same order as every other setting.
export BACKUPDRILL_PING_URL="https://hc-ping.com/<uuid>"
npx backupdrill backupStep 3 — add it to the scheduled job
Cron: put the variable in the environment file your job already loads for its database and bucket settings, and keep that file readable only by you. The check’s Cron schedule then gets the same expression, here 0 3 * * *.
# in ~/.backupdrill.env, next to your database and bucket settings
export BACKUPDRILL_PING_URL="https://hc-ping.com/<uuid>"
# crontab -e
0 3 * * * . "$HOME/.backupdrill.env" && npx -y backupdrill backup >> "$HOME/backupdrill.log" 2>&1GitHub Actions: add a repository secret named BACKUPDRILL_PING_URL (Settings → Secrets and variables → Actions) and pass it to the step that runs the CLI. The workflow template in the CLI repository, examples/scheduled-backup.yml, already forwards it; an unset secret is an empty string, which the CLI treats as no ping URL.
- name: Run backupdrill
run: npx backupdrill backup
env:
BACKUPDRILL_DATABASE_URL: ${{ secrets.BACKUPDRILL_DATABASE_URL }}
# ...the other BACKUPDRILL_* secrets you already pass...
BACKUPDRILL_PING_URL: ${{ secrets.BACKUPDRILL_PING_URL }}Step 4 — run it once and look at the check
Run the backup by hand, or use Run workflow on the Actions tab. The check’s events should show a start, then a success with the run’s duration. Open the success event and you see the summary the CLI sent, along the lines of backup complete, 42 tables + 318 Storage files, snapshot 2026-10-19T03-00-04-117Z. That snapshot name is the one backupdrill drill --snapshot takes.
Then make sure an alert would reach you. A new Healthchecks account starts with an email integration for the address you signed up with, and a new check uses every integration in its project. On the Integrations page, the Test! button next to an integration sends a test alert; add Slack or another channel there if email is not where you look.
What each ping means
Every run of backup or drill sends a start ping and then exactly one of success or failure. The last row is the reason to set this up.
| When | Request | What Healthchecks does |
|---|---|---|
| The run starts | <url>/start | Starts timing the run. If no success or failure follows within Grace Time, the check goes down. |
| The run succeeds | <url> | The check is up. The event shows how long the run took and the one-line summary. |
| The run fails | <url>/fail | The check goes down and you get an alert straight away, with the error in it. |
| Nothing arrives | none | Once the schedule plus Grace Time passes, the check goes down and you get an alert. This is the case logs and exit codes never catch. |
Both requests of a run carry the same run ID in a rid parameter, so Healthchecks matches each finish to its own start even when two runs overlap. The CLI fails a run before any data moves if the configuration is incomplete, and that still sends /fail: a deleted secret shows up as an alert, not as silence.
What the alert email says
Healthchecks puts the body of the last ping into its alert, so the email tells you why the backup failed. Here the runner’s pg_dump was older than the database:
The check "Supabase nightly backup" has gone down.
Reason: received a failure signal.
…
Last ping type: Failure
…
Last Ping Body:
---------------
backupdrill 2.5.0: backup failed. pg_dump is v15 but the server is v17.
pg_dump must be >= the server major version. Install postgresql-client-17
(or newer) and/or point BACKUPDRILL_PG_DUMP at it.A failure message can contain connection details or, from a restore, values out of your tables, so the CLI cleans it first. Connection strings, passwords, hostnames, IP addresses, Supabase project refs and bucket keys become placeholders such as [redacted] and [host], and the parts of Postgres errors that quote row values or SQL (DETAIL, CONTEXT, Command was) are dropped. That catches what it recognises, and a value it doesn’t recognise can still get through. The message is cut at 1,000 characters; the full error is still in the job’s own log.
Watch restore drills with a second check
backupdrill drill restores the latest snapshot into a throwaway Postgres in Docker and checks it. It sends the same pings. A drill that runs but doesn’t pass (a checksum mismatch, a table that came back empty) sends /fail with the failed checks listed in the body, so the alert names what broke.
Give drills their own check. On a shared check, the daily backup’s success pings keep the check up, so a weekly drill that stops running never raises an alert. Pass each command its own URL, for example with --ping-url on the drill’s cron line or a second secret in its workflow.
When Healthchecks can’t be reached
Monitoring should never be the reason a backup didn’t happen. Each attempt times out after 10 seconds, and network and server errors get up to two retries. If it still doesn’t get through, the CLI prints a warning such as Could not send the start ping and carries on. The backup’s output and exit code are the same as without a ping URL. The ping URL itself is never printed, because the job log is often more widely readable than the secret.
FAQ
Does it work with a self-hosted Healthchecks instance?
Yes. Give the CLI your instance's ping URL instead of the hc-ping.com one. The CLI only adds /start or /fail to the end of the path and a run ID to the query string, so slug URLs work too, and a ?create=1 already in the URL is kept.
What does the CLI send to Healthchecks?
Two short requests per run: a start ping with no body, then a success or a failure ping. The success ping carries one line: table count, Storage file count and snapshot name for a backup; tables, rows restored and restore time for a drill. The failure ping carries the error. Before it goes out, the CLI removes connection strings, passwords, hostnames, IP addresses, Supabase project refs and bucket keys, and drops the parts of Postgres errors that quote row values or SQL (DETAIL, CONTEXT and echoed commands). It is a filter, not a guarantee, so send these alerts only where you would also be fine pasting the job's log. The full error stays in that log.
What happens if Healthchecks is down when the backup runs?
The backup runs anyway. Each attempt times out after 10 seconds, network and server errors get up to two retries, and if the ping still doesn't get through, the CLI prints a warning. The exit code is whatever the backup itself returned. Healthchecks may then alert you about a run that actually worked, because the success ping never arrived. That is a false alarm, which is the safer way for monitoring to be wrong.
Do I need this with the hosted BackupDrill service?
No. The hosted service schedules backups and restore drills itself and emails you when one fails. This setup is for running the open-source CLI on your own schedule, from cron or GitHub Actions.
Sources
- Healthchecks.io docs — Pinging API
- Healthchecks.io docs — Configuring checks
- Healthchecks.io docs — Measuring script run time
- Healthchecks.io — Pricing
- Healthchecks source — plain-text alert email template
- GitHub Docs — Events that trigger workflows
- Supabase docs — Project Pausing
- backupdrill CLI — README
Facts and prices last verified October 19, 2026 against the sources above. Written by the team behind BackupDrill.
The hosted BackupDrill service runs the same engine on its own schedule and emails you when a backup or drill fails. The Free plan covers weekly backups for one project; scheduled restore drills start on the Solo plan. Start free, or see the CLI docs to keep running it yourself.