restic-backup-suite is a production-ready backup toolkit for Linux servers built on top of restic. It automates the full backup lifecycle: database dumps, file backup, retention policy enforcement, and integrity verification — all in a single configurable shell script.
Automates the entire backup lifecycle on Linux servers — from database dumps to integrity checks — with a single command.
The toolkit consists of three components:
| File | Purpose |
|---|---|
backup.sh |
Automated backup with retention and verification |
restore.sh |
Interactive restore menu from any snapshot |
install.sh |
System-wide installation with aliases and optional cron job |
set -euo pipefail) throughout all scriptsinstall.sh with shell aliases and optional cron job| Requirement | Version | Notes |
|---|---|---|
| Bash | 4.4+ | Standard on all modern Linux distributions |
| restic | any | Must be in PATH |
| Root access | — | Scripts require sudo |
| sftp | — | Only for SFTP remote backends (e.g. Hetzner Storage Box) |
| docker | — | Only for Docker database dumps |
| mysqldump / mysql | — | Only for native MySQL dumps / restore |
The install.sh script sets everything up system-wide:
git clone https://github.com/markus-michalski/restic-backup-suite.git
cd restic-backup-suite
sudo ./install.sh
After installation:
/etc/restic/config.sh/usr/local/bin/restic-backup and /usr/local/bin/restic-restore/etc/profile.d/restic-aliases.shWith optional cron job (daily at 03:00):
sudo ./install.sh --cron
git clone https://github.com/markus-michalski/restic-backup-suite.git
cd restic-backup-suite
cp config.example.sh config.sh
chmod 600 config.sh
Run scripts directly:
sudo ./backup.sh
sudo ./restore.sh
echo "your-strong-passphrase" > /etc/restic/password.txt
chmod 400 /etc/restic/password.txt
Point RESTIC_PASSWORD_FILE in config.sh to this file:
RESTIC_PASSWORD_FILE="/etc/restic/password.txt"
For SFTP backends (e.g. Hetzner Storage Box), create a dedicated key pair:
ssh-keygen -t ed25519 -f /root/.ssh/id_ed25519_backup -N ""
Copy the public key to the remote server, then set in config.sh:
SSH_KEY_FILE="/root/.ssh/id_ed25519_backup"
backup.sh writes the SSH config entry automatically on the first run.
Configuration lives in config.sh (copied from config.example.sh). This file is gitignored and will never be committed.
config.shcontains sensitive credentials. Set permissions tochmod 600— only root should be able to read it.
| Variable | Description | Default |
|---|---|---|
RESTIC_PASSWORD_FILE |
Path to password file | /etc/restic/password.txt |
RESTIC_REPOSITORY |
Repository URL | — |
RESTIC_CACHE_DIR |
Local cache directory | ~/.cache/restic |
GOMAXPROCS |
CPU cores for restic | 2 |
SSH_HOST |
SSH alias (SFTP backend) | — |
SSH_HOSTNAME |
Real remote hostname | — |
SSH_PORT |
SSH port | 22 |
SSH_USER |
SSH username | — |
SSH_KEY_FILE |
SSH private key path | — |
LOG_DIR |
Log directory | /var/log/restic |
BACKUP_PATHS |
Array of paths to back up | — |
BACKUP_EXCLUDES |
Array of exclude patterns | — |
RETENTION_KEEP_DAILY |
Daily snapshots to keep | 7 |
RETENTION_KEEP_WEEKLY |
Weekly snapshots to keep | 4 |
RETENTION_KEEP_MONTHLY |
Monthly snapshots to keep | 6 |
REPO_CHECK_SUBSET |
Fraction of data to verify | 5% |
MYSQL_BACKUP_ENABLED |
Enable native MySQL dump | false |
DOCKER_MARIADB_CONTAINERS |
MariaDB containers to dump | () |
DOCKER_POSTGRES_CONTAINERS |
PostgreSQL containers to dump | () |
SERVICES_TO_STOP |
Systemd services to pause | () |
# SFTP (Hetzner Storage Box)
RESTIC_REPOSITORY="sftp:myhost:/backup/restic"
# Local
RESTIC_REPOSITORY="/mnt/backup/restic-repo"
# Amazon S3
RESTIC_REPOSITORY="s3:s3.amazonaws.com/bucket-name"
# Backblaze B2
RESTIC_REPOSITORY="b2:bucket-name:/path"
sudo restic-backup --dry-run
# or directly:
sudo ./backup.sh --dry-run
Shows what would be backed up — without running restic.
sudo restic-backup
# or:
sudo ./backup.sh
sudo ./backup.sh --config /etc/restic/config.sh
sudo restic-restore
# or:
sudo ./restore.sh
The menu appears:
What do you want to restore?
----------------------------
1) Full backup (all paths)
2) Web files (/var/www)
3) Configuration (/etc)
4) Home directories (/home)
5) Database dumps (SQL files from backup)
6) SSL certificates (/etc/letsencrypt)
7) Custom path
q) Quit
sudo ./restore.sh --snapshot abc12345
Restored files always land in a temporary directory first (/tmp/restic-restore-XXXXXX). You review them before manually moving anything into place.
After sudo ./install.sh, the following aliases are available after next login (or source /etc/profile.d/restic-aliases.sh):
| Alias | Function |
|---|---|
restic-snapshots |
List all snapshots |
restic-stats |
Repository size and dedup statistics |
restic-check |
Integrity check (5% sample) |
restic-ls |
List files in the latest snapshot |
restic-mount |
Mount repository via FUSE |
restic-unlock |
Remove stale lock |
restic-rawstats |
Raw on-disk size |
Aliases automatically load the configuration from
/etc/restic/config.sh— no manualexportrequired.
| Symptom | Check | Solution |
|---|---|---|
Config file not found |
Does config.sh exist? |
cp config.example.sh config.sh |
Permission denied |
Script executable? | chmod +x backup.sh |
SFTP connection failed |
SSH config correct? | Test with sftp myhost manually |
Wrong password |
Password file correct? | Check: cat /etc/restic/password.txt |
Repository not found |
Repo initialized? | First backup inits automatically, or run restic init |
Lock exists |
Another process running? | Run restic-unlock |
No such snapshot |
Snapshot ID correct? | Run restic-snapshots for the list |
On the first run, the restic repository is initialized automatically — no manual
restic initneeded.
# Check SSH config
grep -A5 "Host myhost" /root/.ssh/config
# Test manual SFTP connection
sftp myhost
# Populate known hosts
ssh-keyscan -p 22 storage.example.com >> /root/.ssh/known_hosts_backup
# Last backup log
tail -100 /var/log/restic/backup-$(date +%Y-%m-%d).log
# Cron log
tail -100 /var/log/restic/cron.log
All scripts follow the same pattern:
set -o errexit -o nounset -o pipefail — Strict modetrap cleanup EXIT ERR INT TERM — Reliable cleanupmain "$@" — Entry point at the end of the filemainconfig.sh — no hardcoded valuesThe script writes SSH configuration to ~/.ssh/config.d/restic-backup and automatically prepends Include ~/.ssh/config.d/* to ~/.ssh/config — without overwriting any existing configuration.
| Exit Code | Meaning |
|---|---|
| 0 | Backup/restore successful |
| 1 | General error (config missing, permission denied, etc.) |
Can I back up multiple servers with one configuration?
No — one config.sh corresponds to one server and one repository. For multiple servers, use separate configs and cron jobs.
What happens if the backup is interrupted?
restic leaves a lock. After restarting, run restic-unlock then restart the backup.
Are database backups consistent?
Yes — database dumps are created before the backup and deleted afterward. The SQL files are included in the backup set.
How much overhead does restic deduplication add?
Very little. Incremental backups typically transfer only 1–5% of the total data size.
Can I mirror the repository to multiple targets?
restic doesn't support native mirroring. Alternative: use rclone as an additional sync step after the backup.
How do I uninstall everything?
sudo ./install.sh --uninstall
Configuration and logs are not deleted (asked separately).
MIT — Free to use, modify, and redistribute. No warranty.