Simple Restic Backup Monitoring with Telegram Notifications
Created:
Introduction
Restic is a powerful, efficient, and secure backup tool that works with local and remote repositories. However, even the best backup solution is useless if you don't verify that backups are actually being created. This article presents a simple yet effective bash script that monitors your restic repositories and sends you daily Telegram notifications about their status.
Important: While this script helps you monitor backup creation, it does not verify that your backups can be restored. Regular restore tests remain essential for a complete backup strategy.
The Monitoring Script
The following script checks all restic repositories in a specified directory, identifies those with outdated snapshots, and sends a formatted report to Telegram:
#!/bin/bash
set -o errexit
set -o nounset
set -o pipefail
TELEGRAM_TOKEN="YOUR_BOT_TOKEN"
TELEGRAM_CHAT_ID="YOUR_CHAT_ID"
readonly LOG_FILE="/var/log/restic-backup-check-telegram.log"
readonly LOG_TIMESTAMP_FORMAT='+%Y-%m-%d %H:%M:%S%z'
today=$(date +%Y-%m-%d)
backup_base="/path/to/your/backup/repos"
all_entries=""
non_today_entries=""
today_date=$(date +%Y-%m-%d)
log_line() {
printf '[%s] %s\n' "$(date "${LOG_TIMESTAMP_FORMAT}")" "$1" >>"${LOG_FILE}"
}
log_stream() {
local line=""
while IFS= read -r line || [[ -n "$line" ]]; do
log_line "$line"
done
}
log_message() {
printf '%s\n' "$1" | log_stream
}
for dir in "$backup_base"/*/; do
latest_file=$(find "$dir"snapshots/ -type f -printf '%T@ %p\n' 2>/dev/null | sort -n | tail -1 | cut -d' ' -f2-)
if [[ -n "$latest_file" ]]; then
latest_date=$(stat -c '%y' "$latest_file" | cut -d' ' -f1)
latest_time=$(stat -c '%y' "$latest_file" | cut -d' ' -f2 | cut -d':' -f1-2)
all_entries+=$(printf "%s %s %s\n" "$latest_date" "$latest_time" "$dir")
all_entries+=$'\n'
if [[ "$latest_date" != "$today_date" ]]; then
non_today_entries+=$(printf "%s %s %s\n" "$latest_date" "$latest_time" "$dir")
non_today_entries+=$'\n'
fi
fi
done
all_entries=$(echo "$all_entries" | sort)
non_today_entries=$(echo "$non_today_entries" | sort)
# Send results via Telegram
HOSTNAME=$(hostname -f 2>/dev/null || hostname)
MESSAGE="[$HOSTNAME] Restic Backup Check ($today)
=== WARNING: Current Backup Status ===
$non_today_entries
=== INFO: Current Backup Status ===
$all_entries
"
URL="https://api.telegram.org/bot$TELEGRAM_TOKEN/sendMessage"
if curl -s -X POST "$URL" -d chat_id="$TELEGRAM_CHAT_ID" -d text="$MESSAGE" >/dev/null; then
log_message "Telegram restic backup status sent."
else
log_message "Warning: failed to send Telegram restic backup status."
fi
# Append to log file with timestamped lines
log_message "$MESSAGE"How It Works
1. Repository Discovery
The script scans the specified backup_base directory for all subdirectories, treating each as a separate restic repository. This matches the common pattern of having one repository per host or system.
2. Snapshot Detection
For each repository, it:
- Finds the most recent file in the
snapshots/directory - Extracts the creation date and time from the file metadata
- Records this information for reporting
3. Status Classification
The script categorizes repositories into two groups:
- WARNING: Repositories whose latest snapshot is not from today
- INFO: All repositories, showing their latest snapshot timestamp
4. Notification
The script sends a formatted message to Telegram using the bot API, including:
- The hostname of the monitoring system
- Current date
- List of repositories with outdated snapshots (WARNING section)
- Complete list of all repositories with their latest snapshot (INFO section)
5. Local Logging
All actions and messages are logged to /var/log/restic-backup-check-telegram.log with timestamps for local auditing.
Example Output
Here's what a typical daily Telegram message looks like:
[professor-farnsworth] Restic Backup Check (2025-12-10)
=== WARNING: Current Backup Status ===
2025-12-09 10:29 /path/to/backup/repos/server-bender/
2025-12-09 00:00 /path/to/backup/repos/server-fry/
=== INFO: Current Backup Status ===
2025-12-09 10:29 /path/to/backup/repos/server-bender/
2025-12-09 00:00 /path/to/backup/repos/server-fry/
2025-12-10 00:00 /path/to/backup/repos/server-leela/
2025-12-10 00:00 /path/to/backup/repos/server-zoidberg/
2025-12-10 00:00 /path/to/backup/repos/server-amy/
2025-12-10 00:00 /path/to/backup/repos/server-hermes/
2025-12-10 00:00 /path/to/backup/repos/server-nibbler/
2025-12-10 00:00 /path/to/backup/repos/server-kif/
2025-12-10 00:00 /path/to/backup/repos/server-scruffy/
2025-12-10 00:00 /path/to/backup/repos/server-calculon/
2025-12-10 00:00 /path/to/backup/repos/server-morbo/
2025-12-10 00:00 /path/to/backup/repos/server-zapp/
In this example, two repositories (server-bender and server-fry) have not been updated today, triggering a warning.
Repository Structure
The script expects a directory structure like this:
/home/backup/repos/
├── restic-server-leela/
│ ├── snapshots/
│ │ ├── 12345678
│ │ └── ...
│ └── ...
├── restic-server-zoidberg/
│ ├── snapshots/
│ └── ...
└── restic-server-nibbler/
├── snapshots/
└── ...
Each subdirectory represents a separate restic repository, typically corresponding to one host or system being backed up.
Issues the script can detect and its limitations
1. Lock File Issues
When managing multiple remote repositories (e.g., 10 remotes with daily snapshots), you may encounter lock file conflicts (once or twice a month from my own experience). Restic uses lock files to prevent concurrent access to a repository. If a backup process crashes or is interrupted, the lock file might not be released, causing subsequent backup attempts to fail.
This script will not detect lock file issues directly, but it may show outdated snapshots if backups are failing due to locks.
2. Snapshot vs. Backup Success
This script only verifies that snapshot files exist and are recent. It does not check:
- Whether the snapshot was created successfully (partial failures may still create files)
- The size or content of the snapshots
- Whether the data within snapshots is valid or restorable
With restic check or restic restore, you can verify the integrity of your backups, but those checks are separate and should be performed on the backup host where restic secrets are available.
3. No Restore Verification
This monitoring approach does not test whether your backups can actually be restored. A snapshot file might exist but be corrupted, or the repository might have undetected issues.
4. Repository Access Permissions
The script requires read access to the repository directories and files (not the snapshots itself):
- The user running the script has appropriate permissions to access the
snapshots/directories and read file metadata - If running as a cron job, ensure the environment and permissions are correctly set up for the script to access the repositories
Setup Instructions
1. Create a Telegram Bot
- Open Telegram and search for @BotFather
- Send
/newbotcommand - Follow the prompts to name your bot and get your TELEGRAM_TOKEN
- Start a chat with your new bot
- Send any message to the bot, then visit
https://api.telegram.org/bot<YOUR_TOKEN>/getUpdatesto find your TELEGRAM_CHAT_ID
2. Configure the Script
- Replace
YOUR_BOT_TOKENwith your actual Telegram bot token - Replace
YOUR_CHAT_IDwith your chat ID (use a group chat ID for team notifications) - Set
backup_baseto your restic repositories directory - Adjust
LOG_FILEif you want logs stored elsewhere
3. Make It Executable
chmod +x restic-monitor.sh4. Test the Script
./restic-monitor.sh
Check your Telegram for the test message.
5. Schedule Daily Execution
Add a cron job to run the script daily:
0 8 * * * /path/to/restic-monitor.sh
This example runs the check at 8 AM every day.
Conclusion
This simple bash script provides an effective way to monitor your restic backup repositories and receive daily status updates via Telegram. While it has important limitations—particularly around snapshot integrity and lack of restore verification—it serves as a valuable first line of defense in ensuring your backups are being created as expected.
Because this script does not need any restic repository secrets, the secrets can stay securely on the backup hosts. Integrity checks and restore tests can be performed separately; on the backup host which is the only system that has the restic secrets for its own repository. This separation of concerns allows for a more secure and efficient monitoring setup, while still ensuring that you are aware of any issues with backup creation in a timely manner.
Resources
Feedback
Have thoughts or experiences you'd like to share? I'd love to hear from you! Whether you agree, disagree, or have a different perspective, your feedback is always welcome. Drop me an email and let's start a conversation.