restic-backup-suite ist ein produktionsreifes Backup-Toolkit für Linux-Server, das auf restic basiert. Es automatisiert den vollständigen Backup-Lifecycle: Datenbank-Dumps, Datei-Backup, Retention-Policy, Integritätsprüfung — alles in einem konfigurierbaren Shell-Script.
Automatisiert den gesamten Backup-Lifecycle auf Linux-Servern — von Datenbank-Dumps bis zur Integritätsprüfung — mit einem einzigen Befehl.
Das Toolkit besteht aus vier Komponenten:
| Datei | Funktion |
|---|---|
backup.sh |
Automatisches Backup mit Retention und Verifikation |
restore.sh |
Interaktives Restore-Menü aus beliebigen Snapshots |
inspect.sh |
Snapshot-Inhalte interaktiv durchsuchen ohne Restore |
install.sh |
System-Installation mit Aliases und optionalem Cron-Job |
set -euo pipefail) in allen Scriptsinstall.sh mit Shell-Aliases und optionalem Cron-Job| Anforderung | Version | Hinweise |
|---|---|---|
| Bash | 4.4+ | Standard auf modernen Linux-Distributionen |
| restic | beliebig | Muss im PATH vorhanden sein |
| Root-Zugriff | — | Scripts benötigen sudo |
| sftp | — | Nur für SFTP-Remote-Backends (z.B. Hetzner Storage Box) |
| docker | — | Nur für Docker-Datenbank-Dumps und Docker-Service-Stops |
| mysqldump / mariadb-dump | — | Nur für native MySQL/MariaDB-Dumps. Binary wird automatisch erkannt. |
| mysql / mariadb | — | Nur für nativen Dump (Datenbank-Auflistung) und Restore. Binary wird automatisch erkannt. |
Das install.sh Script richtet alles systemweit ein:
git clone https://github.com/markus-michalski/restic-backup-suite.git
cd restic-backup-suite
sudo ./install.sh
Danach stehen bereit:
/etc/restic/config.sh/usr/local/bin/restic-backup, /usr/local/bin/restic-restore und /usr/local/bin/restic-inspect/etc/profile.d/restic-aliases.shMit optionalem Cron-Job (täglich 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
Scripts direkt aufrufen:
sudo ./backup.sh
sudo ./restore.sh
sudo ./inspect.sh
echo "dein-starkes-passwort" > /etc/restic/password.txt
chmod 400 /etc/restic/password.txt
In config.sh auf diese Datei verweisen:
RESTIC_PASSWORD_FILE="/etc/restic/password.txt"
Für SFTP-Backends (z.B. Hetzner Storage Box) einen dedizierten Key anlegen:
ssh-keygen -t ed25519 -f /root/.ssh/id_ed25519_backup -N ""
Public Key auf den Remote-Server kopieren, dann in config.sh:
SSH_KEY_FILE="/root/.ssh/id_ed25519_backup"
backup.sh schreibt den SSH-Config-Eintrag automatisch beim ersten Aufruf.
Die Konfiguration liegt in config.sh (aus config.example.sh kopieren). Diese Datei ist gitignored und wird nie committed.
config.shenthält sensible Zugangsdaten. Berechtigungen aufchmod 600setzen — nur root darf lesen.
| Variable | Beschreibung | Standard |
|---|---|---|
RESTIC_PASSWORD_FILE |
Pfad zur Passwort-Datei | /etc/restic/password.txt |
RESTIC_REPOSITORY |
Repository-URL | — |
RESTIC_CACHE_DIR |
Lokales Cache-Verzeichnis | ~/.cache/restic |
GOMAXPROCS |
CPU-Kerne für restic | 2 |
SSH_HOST |
SSH-Alias (SFTP-Backend) | — |
SSH_HOSTNAME |
Echter Remote-Hostname | — |
SSH_PORT |
SSH-Port | 22 |
SSH_USER |
SSH-Benutzername | — |
SSH_KEY_FILE |
Pfad zum SSH-Private-Key | — |
LOG_DIR |
Log-Verzeichnis | /var/log/restic |
BACKUP_PATHS |
Array der Backup-Pfade | — |
BACKUP_EXCLUDES |
Array der Exclude-Pattern | — |
RETENTION_KEEP_DAILY |
Tägliche Snapshots behalten | 7 |
RETENTION_KEEP_WEEKLY |
Wöchentliche Snapshots behalten | 4 |
RETENTION_KEEP_MONTHLY |
Monatliche Snapshots behalten | 6 |
REPO_CHECK_SUBSET |
Anteil der Daten zur Verifikation | 5% |
MYSQL_BACKUP_ENABLED |
Nativen MySQL/MariaDB-Dump aktivieren | false |
DOCKER_MARIADB_CONTAINERS |
MariaDB-/MySQL-Container für Dumps. Format: "container:user:db[:PW_ENV]" — optionales 4. Feld für Passwort-Env-Var (Standard: MYSQL_ROOT_PASSWORD) |
() |
DOCKER_POSTGRES_CONTAINERS |
PostgreSQL-Container für Dumps | () |
SERVICES_TO_STOP |
Systemd-Services zum Pausieren | () |
DOCKER_SERVICES_TO_STOP |
Docker-Compose-Services oder Standalone-Container vor dem Backup stoppen. Format: "/pfad/compose.yml:service-name" oder "container-name". Kein DB-Container. |
() |
DOCKER_STOP_WAIT |
Wartezeit nach Docker-Service-Stop (Sekunden) | 2 |
DOCKER_START_WAIT |
Wartezeit nach Docker-Service-Start (Sekunden) | 3 |
SUCCESS_MARKER_FILE |
Pfad zur Marker-Datei nach erfolgreichem Backup. Leer setzen zum Deaktivieren. | /var/backup/.last_restic_success |
# SFTP (Hetzner Storage Box)
RESTIC_REPOSITORY="sftp:myhost:/backup/restic"
# Lokal
RESTIC_REPOSITORY="/mnt/backup/restic-repo"
# Amazon S3
RESTIC_REPOSITORY="s3:s3.amazonaws.com/bucket-name"
# Backblaze B2
RESTIC_REPOSITORY="b2:bucket-name:/path"
DOCKER_MARIADB_CONTAINERS=(
"wikijs-db:root:mm_faq" # nutzt MYSQL_ROOT_PASSWORD
"mmkreativ-db:root:mm_live:DB_ROOT_PASS" # eigener Env-Var-Name
"another-db:root:shop_db:MARIADB_ROOT_PASSWORD" # neuere offizielle Images
)
Format: "container_name:db_user:db_name[:password_env_var]"
Das optionale 4. Feld benennt die Env-Var im Container, die das Root-Passwort enthält. Standard: MYSQL_ROOT_PASSWORD. Neuere offizielle MariaDB-Images nutzen MARIADB_ROOT_PASSWORD, eigene Setups ggf. DB_ROOT_PASS o.ä.
backup.sherkennt automatisch obmariadb-dumpodermysqldumpim Container verfügbar ist — gilt auch für native Dumps auf dem Host.
Mit DOCKER_SERVICES_TO_STOP werden App-Container vor dem Datei-Backup gestoppt und danach automatisch neu gestartet. Das verhindert inkonsistente Snapshots, wenn der Container während des Backups in sein Datenverzeichnis schreibt.
DB-Container nicht in diese Liste aufnehmen — Datenbankdumps laufen gegen den laufenden Container. Nur App-Container stoppen.
DOCKER_SERVICES_TO_STOP=(
"/srv/seafile/docker-compose.yml:seafile" # Compose-Service
"my-standalone-container" # Standalone-Container
)
Unterstützte Formate pro Eintrag:
"/pfad/zu/docker-compose.yml:service-name" — Compose-verwalteter Service (docker compose stop/start)"container-name" — Standalone-Container gestartet via docker run (docker stop/start)Das Feature ist opt-in: eine leere Liste (Standard) ändert das bestehende Verhalten nicht. Container werden auch beim Abbruch des Backups (Fehler, Strg+C) zuverlässig neu gestartet — der Restart ist im Cleanup-Trap verankert.
Nach jedem erfolgreichen restic-Backup schreibt backup.sh einen ISO-Timestamp in die Datei SUCCESS_MARKER_FILE (Standard: /var/backup/.last_restic_success). Das Verzeichnis wird automatisch angelegt, falls nicht vorhanden. Die Datei erhält Berechtigungen 600.
Externe Monitoring-Tools prüfen das Datei-Alter gegen das konfigurierte Backup-Intervall:
# Alert wenn Datei älter als 24 h (für tägliche Backups)
find /var/backup/.last_restic_success -mmin +1440 && echo CRITICAL || echo OK
Verhalten:
--dry-run: Datei wird nicht geschrieben--dump-only: Datei wird nicht geschrieben (kein restic-Backup gelaufen)Eine veraltete Marker-Datei bedeutet: entweder ist das Backup fehlgeschlagen, oder der Marker konnte nicht geschrieben werden. Das Log gibt Aufschluss.
Zum Deaktivieren in config.sh:
SUCCESS_MARKER_FILE=""
sudo restic-backup --dry-run
# oder direkt:
sudo ./backup.sh --dry-run
Zeigt, was gesichert würde — ohne restic auszuführen. Services und Container werden beim Dry Run nicht gestoppt.
sudo restic-backup
# oder:
sudo ./backup.sh
sudo ./backup.sh --config /etc/restic/config.sh
sudo restic-backup --dump-only
# oder direkt:
sudo ./backup.sh --dump-only
Führt alle konfigurierten Datenbank-Dumps aus (native MySQL, Docker MariaDB/PostgreSQL) und zeigt Ergebnis mit Größenangaben — ohne restic, SFTP oder Service-Stops. Ideal zum Verifizieren neuer Container-Konfigurationen.
sudo restic-restore
# oder:
sudo ./restore.sh
Das Menü erscheint:
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
Wiederhergestellte Dateien landen immer zuerst in einem temporären Verzeichnis (/tmp/restic-restore-XXXXXX). Erst nach Prüfung werden sie manuell an den Zielort verschoben.
sudo restic-inspect
# oder direkt:
sudo ./inspect.sh
Beim Start erscheint die Snapshot-Auswahl — alle verfügbaren Snapshots werden aufgelistet. Enter wählt latest, alternativ eine Snapshot-ID eingeben.
Danach das interaktive Menü:
Snapshot: latest
================================
1) Show snapshot details
2) Show statistics (size)
3) List files (with optional path filter)
4) Search files by name/pattern
5) Diff against another snapshot
s) Select a different snapshot
q) Quit
sudo restic-inspect latest
sudo restic-inspect abc12345
# oder:
sudo ./inspect.sh --snapshot abc12345
| Auswahl | Funktion |
|---|---|
1 |
Snapshot-Details und Metadaten anzeigen |
2 |
Statistiken: Restore-Größe des Snapshots |
3 |
Dateien auflisten — mit optionalem Pfad-Filter (z.B. /var/www) |
4 |
Dateinamen nach Substring suchen (z.B. wp-config, .sql) |
5 |
Diff zwischen diesem und einem anderen Snapshot |
s |
Anderen Snapshot auswählen, ohne das Programm zu beenden |
q |
Beenden |
inspect.shstellt keine Dateien wieder her — es dient ausschließlich zur Inspektion. Für Wiederherstellungrestore.shverwenden.
Nach sudo ./install.sh stehen nach dem nächsten Login (oder source /etc/profile.d/restic-aliases.sh) folgende Aliases zur Verfügung:
| Alias | Funktion |
|---|---|
restic-snapshots |
Alle Snapshots auflisten |
restic-stats |
Repository-Größe und Dedup-Statistiken |
restic-check |
Integritätsprüfung (5% Sample) |
restic-ls |
Dateien im neuesten Snapshot auflisten |
restic-mount |
Repository via FUSE mounten |
restic-unlock |
Veralteten Lock entfernen |
restic-rawstats |
Rohe On-Disk-Größe |
Aliases laden automatisch die Konfiguration aus
/etc/restic/config.sh— kein manuellesexportnötig.
| Symptom | Prüfen | Lösung |
|---|---|---|
Config file not found |
Existiert config.sh? |
cp config.example.sh config.sh |
Permission denied |
Script ausführbar? | chmod +x backup.sh |
SFTP connection failed |
SSH-Config korrekt? | sftp myhost manuell testen |
Wrong password |
Passwort-Datei korrekt? | Inhalt prüfen: cat /etc/restic/password.txt |
Repository not found |
Repo initialisiert? | Erstes Backup init automatisch, oder restic init |
Lock exists |
Anderer Prozess läuft? | restic-unlock aufrufen |
No such snapshot |
Snapshot-ID korrekt? | restic-snapshots für Liste |
mysqldump: not found in Docker-Dump |
Neueres MariaDB-Image ohne Legacy-Binary | Kein Fix nötig — mariadb-dump wird automatisch erkannt |
Access denied (using password: NO) |
Container nutzt anderen Env-Var-Namen als MYSQL_ROOT_PASSWORD |
4. Feld in config.sh: "container:root:db:DB_ROOT_PASS" |
Neither mariadb-dump nor mysqldump found |
Kein Dump-Binary auf dem Host? | apt install mariadb-client oder apt install mysql-client |
docker compose file not found in Docker-Stop |
Pfad in DOCKER_SERVICES_TO_STOP falsch? |
Absoluten Pfad zur docker-compose.yml prüfen |
| Monitoring-Alert: Marker-Datei veraltet | Backup fehlgeschlagen oder Schreibfehler? | Log prüfen: /var/log/restic/backup-YYYY-MM-DD.log |
Beim ersten Aufruf wird das restic-Repository automatisch initialisiert — kein manuelles
restic initnötig.
# SSH-Config prüfen
grep -A5 "Host myhost" /root/.ssh/config
# Manuelle SFTP-Verbindung testen
sftp myhost
# Known Hosts befüllen
ssh-keyscan -p 22 storage.example.com >> /root/.ssh/known_hosts_backup
# Letztes Backup-Log
tail -100 /var/log/restic/backup-$(date +%Y-%m-%d).log
# Cron-Log
tail -100 /var/log/restic/cron.log
Alle Scripts folgen dem gleichen Pattern:
set -o errexit -o nounset -o pipefail — Strict Modetrap cleanup EXIT ERR INT TERM — Zuverlässige Aufräumlogikmain "$@" — Einstiegspunkt am Ende der Dateimain deklariertconfig.sh — keine hardcoded WerteDas Script schreibt SSH-Konfiguration nach ~/.ssh/config.d/restic-backup und fügt automatisch Include ~/.ssh/config.d/* am Anfang von ~/.ssh/config ein — ohne bestehende Konfiguration zu überschreiben.
| Exit Code | Bedeutung |
|---|---|
| 0 | Backup/Restore erfolgreich |
| 1 | Allgemeiner Fehler (Config fehlt, Permission denied, etc.) |
Kann ich mehrere Server mit einer Konfiguration sichern?
Nein — eine config.sh entspricht einem Server und einem Repository. Für mehrere Server separate Configs und Cron-Jobs anlegen.
Was passiert, wenn das Backup unterbrochen wird?
restic hinterlässt einen Lock. Nach einem Neustart restic-unlock aufrufen, dann das Backup erneut starten. Gestoppte Docker-Container werden durch den Cleanup-Trap automatisch neu gestartet — auch bei Fehler oder Strg+C.
Werden Datenbanken konsistent gesichert?
Ja — Datenbank-Dumps werden vor dem Backup erstellt und anschließend gelöscht. Die SQL-Dateien landen im Backup-Set.
Warum Docker-Container stoppen statt einfach dumpen?
DB-Dumps reichen für Datenbanken. Aber App-Container wie Seafile schreiben auch in Blob-Storage-Verzeichnisse (z.B. seafile-data), die kein SQL sind. Nur ein sauberer Stop garantiert dort konsistente Snapshots.
Wie groß ist der Overhead durch restic-Deduplizierung?
Sehr gering. Inkrementelle Backups sichern typischerweise nur 1–5% der Gesamtdatenmenge.
Kann ich das Repository auf mehreren Medien spiegeln?
restic unterstützt kein natives Mirroring. Alternative: rclone als zusätzlicher Sync-Schritt nach dem Backup.
Wie deinstalliere ich alles wieder?
sudo ./install.sh --uninstall
Konfiguration und Logs werden dabei nicht gelöscht (auf Nachfrage).
MIT — Freie Nutzung, Modifikation und Weiterverteilung. Keine Garantie.