---
title: "Restic backup monitoring with Telegram alerts | Logdash"
description: "A restic script that runs backup and check, moves a stamp only when both pass, and a heartbeat that alerts when it goes stale. Plus when Healthchecks.io fits better."
url: https://logdash.io/cron-monitoring/restic-backup
---

# Restic backup monitoring

Chain restic backup, restic check and a timestamp so the stamp only moves when both commands exit 0, then let a heartbeat ping Logdash while the stamp is under 26 hours old, and a failed, partial or skipped backup becomes a Telegram alert.

Restic sends no notifications. It exits with a code, writes to whatever log you pointed it at, and goes quiet. That is fine on the nights it works. On the night the NAS stops accepting the SSH key, restic backup exits non-zero and saves nothing, and it does the same the next night and every night after, while the log grows and nobody reads it. Most people find out on the day they need to restore.

## The restic backup check script

 /usr/local/bin/restic-backup.sh 

```bash
#!/bin/sh
export RESTIC_REPOSITORY=sftp:backup@nas:/srv/restic
export RESTIC_PASSWORD_FILE=/etc/restic/password

restic backup /srv /etc --exclude-caches \
  && restic check --read-data-subset=5% \
  && touch /var/lib/heartbeat/restic
```

Three details carry the weight. The && chain means the stamp moves only if both commands exit 0\. Exit code 3 matters most: restic made a snapshot but could not read some files, and counting that as a failure is how you hear about the database file that was locked every night. And --read-data-subset=5% downloads and verifies about 5% of the pack data per run, around 10 GB a night on a 200 GB repository, so budget the egress if the repository lives in a cloud bucket.

## Why restic monitoring needs a heartbeat

Logdash watches this with a push monitor, a Pro plan feature. It has no schedule or grace setting: on Pro it expects a ping in every 15-second window, alerts on the first empty one and recovers on the next ping. A single curl at the end of a nightly backup would read as down nearly all day. So the script touches a stamp, and a second crontab line turns the age of that stamp into a steady heartbeat.

 crontab -e 

```bash
0 3 * * * /usr/local/bin/restic-backup.sh >>/var/log/restic-backup.log 2>&1
* * * * * for i in $(seq 12); do find /var/lib/heartbeat/restic -mmin -1560 2>/dev/null | grep -q . && curl -fsS -m 4 -o /dev/null -X POST https://api.logdash.io/ping/68b4c1f0e3a2d5c7b9f01234; sleep 5; done
```

The -mmin -1560 is 26 hours: one nightly run plus two hours for a slow upload. Past that the pings stop and the alert goes out within 30 seconds. If the machine running restic dies outright, cron dies with it and the alert comes just as fast, without waiting for the threshold.

## Restic backup notification in three steps

1. **Create a push monitor** On Pro, add a service named after the repository, set its monitor to push and copy the ping URL into the heartbeat line.
2. **Run the script once by hand** Create /var/lib/heartbeat, then run the script. The first check takes the longest. When it finishes the stamp exists, the heartbeat starts and the monitor goes up.
3. **Break it on purpose** Connect Telegram to the monitor, then backdate the stamp with touch -d '2 days ago'. Within 30 seconds Telegram shows the repository name, is down, and Did not receive call for this time range. In real life the same message arrives about two hours after a missed night.

### Logdash vs Healthchecks.io for restic

| Feature                          | Logdash                        | Healthchecks.io                                    |
| -------------------------------- | ------------------------------ | -------------------------------------------------- |
| Nightly schedule                 | Encoded in the stamp threshold | Period or cron expression, plus grace, per check   |
| Restic output in the alert       | Not stored                     | Ping body up to 100 kB, so the log rides along     |
| Exit code reporting              | Silence only                   | Ping with the exit status, non-zero alerts at once |
| Backup host dies                 | Alert within 30 seconds        | Alert after the period plus grace                  |
| Uptime checks and logs alongside | HTTP monitors and eight SDKs   | Heartbeats only                                    |
| Price for one nightly backup     | Push monitors need Pro         | Free, 20 checks                                    |

### When Healthchecks.io is the better pick

- You run restic on many machines with different schedules. A cron expression per check is less to maintain than a threshold in every crontab.
- You want the restic output attached to the alert, so the failing line is the first thing you read.
- You want the alert by email. Logdash sends Telegram and webhooks only.
- Backups are the only thing you monitor. Healthchecks.io does it for free, and Logdash push monitors start at Pro.

### Does restic send a backup notification on failure? 

No. Restic has no notification setting of its own. It returns an exit code, 0 for success, 3 when some files could not be read, 11 when it could not lock the repository, 12 for a wrong password, and leaves alerting to whatever runs it.

### What does a restic backup check verify? 

restic check verifies that snapshots, trees and pack files are structurally consistent. It reads no file contents unless you add --read-data or --read-data-subset. A small subset every night plus an occasional full read is a common split.

### What is the simplest restic monitoring setup? 

A shell script chained with &&, a stamp file and one heartbeat line in cron. No agent, no wrapper, no extra binary, only curl. Tools like resticprofile add scheduling and hooks if you outgrow that.

### Does restic backup monitoring catch a backup that never started? 

With a stamp, yes. A cron entry that never fired, a deleted script and a full disk all leave the stamp to age, and the alert goes out once it passes 26 hours. A powered-off host is caught within 30 seconds.

## Keep reading

[All cron monitoring guides](https://logdash.io/cron-monitoring): One guide per scheduler, each with the code that proves a job ran and the alert for when it did not.

[pg\_cron monitoring](https://logdash.io/cron-monitoring/pg-cron): pg\_cron writes every run to cron.job\_run\_details and never alerts on it, so monitor it with a second pg\_cron job that pings an external monitor every 10 seconds, but only while the last finished run of your job succeeded inside the window you expect.

[Laravel scheduler monitoring](https://logdash.io/cron-monitoring/laravel-scheduler): Add a 10-second heartbeat task that posts to an external monitor only while your job last succeeded within a set window, so one alert covers both a failed task and a scheduler that is not running at all.

[Celery beat monitoring](https://logdash.io/cron-monitoring/celery-beat): Have beat send a canary task every 10 seconds that posts to a Logdash push monitor, and the first 15 seconds without a ping tells you beat, the broker or every worker has stopped.

[Uptime monitoring](https://logdash.io/features/monitoring): HTTP checks as often as every 15 seconds. When one fails, Telegram tells you before your users do.
