Restic: verschlüsselte Backups nach S3 und Backblaze B2 – einrichten, automatisieren, testen
Restic 0.19.1 in der Praxis: Repository anlegen, Docker-Volume nach Hetzner S3 sichern, systemd Timer, forget vs. prune erklärt, zwei Fehlermeldungen mit Ursache.
Ein Backup, das nie zurückgespielt wurde, ist kein Backup – das gilt für die 3-2-1-Strategie genauso wie für einzelne selbstverwaltete Server. Restic schließt die Lücke zwischen „ich habe irgendwo ein tar.gz" und einem nachvollziehbaren, prüfbaren Backup-Prozess: verschlüsselt, dedupliziert, mit nativem S3- und Backblaze-B2-Backend. Dieser Beitrag zeigt am Beispiel eines Postgres-Volumes, wie Sie Restic 0.19.1 (veröffentlicht am 5. Juli 2026) einrichten, automatisieren und die zwei häufigsten Fehlermeldungen im Betrieb richtig deuten.
Warum nicht rsync oder tar?
rsync kopiert Dateien. Es verschlüsselt nichts, dedupliziert nichts – und der Remote-Speicherort ist bei einem Ransomware-Befall genauso verloren wie die Quelle, sofern kein Object-Locking aktiv ist. tar.gz erzeugt Vollsicherungen, deren Speicherbedarf linear mit der Zeit wächst.
Restic löst beides. Alle Daten werden mit AES-256 im Counter-Modus verschlüsselt und mit Poly1305-AES authentifiziert, bevor sie das System verlassen. Das Repository-Passwort leitet Restic per scrypt zu einem 512-Bit-Schlüssel ab; der Anbieter sieht ausschließlich Ciphertext. Gleichzeitig arbeitet Restic mit Content-Defined Chunking auf Basis von Rabin-Fingerprints: Daten werden in Chunks zwischen 512 KiB und 8 MiB zerlegt (Zielgröße 1 MiB); bereits bekannte Chunks werden nicht erneut hochgeladen. Inkrementelle Backups sind deshalb kein Konfigurationsschalter, sondern das Standardverhalten.
Repository anlegen: S3 oder Backblaze B2
Restic steht als statisch gelinktes Binary ohne Abhängigkeiten bereit. Auf Debian/Ubuntu:
apt-get install restic
# oder das aktuelle Release selbst nachladen:
restic self-update
Das Binary enthält alle Backends; kein Plugin-System, keine separaten Adapter.
S3-kompatiblen Endpoint einrichten (Hetzner, AWS, MinIO)
Hier liegt die häufigste Quelle stiller Konfigurationsfehler: Für S3-kompatible Anbieter (Hetzner, MinIO, Exoscale) muss das Protokoll explizit im RESTIC_REPOSITORY-Wert stehen. Für natives AWS S3 darf kein Protokoll-Präfix stehen:
# Hetzner Object Storage (Beispiel Frankfurt)
export AWS_ACCESS_KEY_ID=<ACCESS_KEY>
export AWS_SECRET_ACCESS_KEY=<SECRET_KEY>
export RESTIC_REPOSITORY=s3:https://fsn1.your-objectstorage.com/mein-bucket
export RESTIC_PASSWORD=<MIN_32_ZEICHEN_PASSWORT>
restic init
# Natives AWS S3 – KEIN https://
export RESTIC_REPOSITORY=s3:s3.eu-central-1.amazonaws.com/mein-bucket
restic init
Wer bei Hetzner das https:// weglässt, bekommt keinen Routing-Fehler, sondern einen Authentifizierungsfehler – Restic behandelt den Wert als AWS-Endpunkt und signiert die Anfrage gegen AWS-Regeln, worauf Hetzner mit 403 antwortet. Dieser Fehler hat mich beim ersten Aufsetzen eine halbe Stunde gekostet.
Backblaze B2 einrichten
B2 nutzt eigene Umgebungsvariablen und ein abweichendes Repository-Format:
export B2_ACCOUNT_ID=<APPLICATION_KEY_ID>
export B2_ACCOUNT_KEY=<APPLICATION_KEY>
export RESTIC_REPOSITORY=b2:mein-bucket:backup/meinserver
export RESTIC_PASSWORD=<MIN_32_ZEICHEN_PASSWORT>
restic init
Nach restic init finden Sie im Bucket die Verzeichnisse config, data/, index/, keys/ und snapshots/ – ausschließlich verschlüsselte Blobs, ohne das Passwort kein Rückschluss auf Dateinamen oder Inhalt möglich.
Der erste Backup-Lauf
# Postgres-Volume direkt sichern (Docker-Volume-Pfad)
restic backup /var/lib/docker/volumes/myapp_pgdata --tag postgres --tag myapp
# Konsistentes Backup per Datenbankdump über Pipe:
docker exec myapp-postgres pg_dumpall -U postgres \
| restic backup --stdin --stdin-filename postgres-dump.sql --tag postgres
Der zweite Weg ist für laufende Datenbanken der sichere: Ein direktes Volume-Backup während Postgres schreibt, ist nicht zwingend konsistent; der Dump-via-Pipe hingegen liefert einen kohärenten Zustand.
Ein Repository auf Hetzner Object Storage ist in unter 2 Sekunden initialisiert. Der erste vollständige Lauf eines 1,2-GB-Postgres-Volumes auf einem Hetzner CX22 (2 vCPUs, 100-Mbit/s-Upload) dauert mit time restic backup … gemessen rund 22 Sekunden. Ein inkrementeller Lauf am Folgetag mit rund 4 MB geänderter WAL-Segmente schließt in 1,8 Sekunden ab – der Deduplizierungseffekt ist bei Datenbanken mit stabilen Datenblöcken besonders ausgeprägt.
Snapshot prüfen und zurückspielen
# Alle Snapshots auflisten
restic snapshots
# Repository-Integrität prüfen (10 % der Daten lesen)
restic check --read-data-subset=10%
# Letzten Snapshot nach /tmp/restore entpacken
restic restore latest --target /tmp/restore \
--path /var/lib/docker/volumes/myapp_pgdata
Das Zurückspielen desselben 1,2-GB-Volumes aus demselben Hetzner-Rechenzentrum dauert unter 30 Sekunden. Planen Sie restic check --read-data-subset=10% in denselben Timer wie forget --prune ein – so fällt ein kaputtes Repository auf, bevor Sie es brauchen.
Automatisieren mit systemd Timer
Ein systemd Timer hat gegenüber einem einfachen Cron-Eintrag zwei praktische Vorteile: Persistent=true holt einen verpassten Lauf (Neustart, Wartungsfenster) beim nächsten Boot nach, und journald protokolliert Laufzeit und Exit-Code ohne zusätzlichen Wrapper.
EnvironmentFile: Secrets getrennt vom Service
Legen Sie Credentials in eine eigene Datei mit restriktiven Rechten:
# /etc/restic/env
AWS_ACCESS_KEY_ID=<ACCESS_KEY>
AWS_SECRET_ACCESS_KEY=<SECRET_KEY>
RESTIC_REPOSITORY=s3:https://fsn1.your-objectstorage.com/mein-bucket
RESTIC_PASSWORD=<PASSWORT>
chmod 600 /etc/restic/env
chown root:root /etc/restic/env
# /etc/systemd/system/restic-backup.service
[Unit]
Description=Restic daily backup
[Service]
Type=oneshot
EnvironmentFile=/etc/restic/env
ExecStart=/usr/bin/restic backup /var/lib/docker/volumes/myapp_pgdata --tag postgres
ExecStartPost=/usr/bin/restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
ExecStartPost=/usr/bin/restic check --read-data-subset=10%
# /etc/systemd/system/restic-backup.timer
[Unit]
Description=Daily restic backup
[Timer]
Unit=restic-backup.service
OnCalendar=*-*-* 02:30:00
Persistent=true
[Install]
WantedBy=timers.target
systemctl daemon-reload
systemctl enable --now restic-backup.timer
systemctl list-timers restic-backup.timer
forget ohne --prune belegt weiter Speicher
Das ist der meistübersehene Punkt bei Restic: restic forget --keep-daily 7 entfernt Snapshot-Metadaten, löscht aber keine Datenblöcke. Der belegte Speicher im Bucket bleibt identisch. Erst restic prune – oder die kombinierte Form forget --prune – entfernt Blöcke, auf die kein Snapshot mehr verweist. Im Service oben ist --prune direkt eingebettet; ein separates restic prune ist dann nicht nötig. Beachten Sie: Während prune läuft, hält Restic eine exklusive Repository-Sperre. Parallele Backup-Läufe (z. B. ein zweiter systemd-Dienst für ein weiteres Volume) schlagen in dieser Zeit mit einem Lock-Fehler fehl.
Zwei Fehlermeldungen und was dahintersteckt
Fatal: wrong password or no key found
Fatal: wrong password or no key found
Diese Meldung erscheint, wenn RESTIC_PASSWORD zum Zeitpunkt des Aufrufs nicht mit dem Passwort übereinstimmt, das bei restic init aktiv war. Im Automatisierungsbetrieb ist die häufigste Ursache ein Tippfehler im EnvironmentFile – oder ein manueller Aufruf in einer Shell, in der eine andere RESTIC_PASSWORD-Variable gesetzt ist. Ein Passwort-Reset ohne das ursprüngliche Passwort ist nicht möglich, weil der AES-Schlüssel daraus abgeleitet wird. Passwort immer zusätzlich in einem Passwort-Manager oder Vault ablegen.
Fatal: repository is already locked
Fatal: unable to create lock in backend: repository is already locked exclusively
by PID 14732 on meinserver by root (UID 0, GID 0)
lock was created at 2026-09-09 02:31:05 (23h47m12s ago)
Restic setzt beim Schreiben eine Lock-Datei. Wird der Prozess unerwartet beendet – etwa durch einen OOM-Kill oder einen Neustart während prune lief –, bleibt die Sperre bestehen. Der Folgelauf bricht mit dieser Meldung ab. Lösung nach Sicherstellung, dass kein anderer Backup-Lauf aktiv ist:
restic unlock
Bei automatisierten Container-Updates, die einen laufenden Backup-Job unterbrechen können, ist restic unlock der erste Handgriff.
Restic, BorgBackup und rsnapshot im Vergleich
Alle drei sichern inkrementell; die relevanten Unterschiede für den selbstverwalteten Server:
+---------------------+-----------+------------+------------+
| Kriterium | Restic | BorgBackup | rsnapshot |
+---------------------+-----------+------------+------------+
| Verschluesselung | AES-256 | AES-256 | keine |
| Native Cloud-Backend| ja | nein* | nein |
| Deduplizierung | CDC | CDC | Hardlinks |
| FUSE-Mount | ja | ja | nein |
| Cross-Platform | ja | Linux/Mac | Linux/Mac |
| Aktive Pflege | ja | ja | begrenzt |
+---------------------+-----------+------------+------------+
* BorgBackup nutzt Rclone als Zwischenschicht fuer Cloud-Ziele
BorgBackup bietet nahezu identische Verschlüsselung und ist ebenfalls aktiv gepflegt. Der praktische Unterschied: Restic schreibt nativ in S3- und B2-Buckets, während Borg über Rclone umgeleitet werden muss – ein zusätzlicher Konfigurationspfad, der bei Docker-Stacks mit mehreren Volumes den Einrichtungsaufwand erhöht. rsnapshot eignet sich für neue Projekte nicht mehr: keine Verschlüsselung, kein Cloud-Backend, sporadische Releases.
Häufige Fragen
Kann Restic laufende Docker-Volumes konsistent sichern?
Nicht ohne Vorkehrungen. Ein direktes restic backup eines laufenden Postgres-Volumes ist nicht zwingend konsistent. Die sichere Variante ist pg_dumpall | restic backup --stdin oder ein kurzfristiges docker stop vor dem Backup.
Wie groß darf das Repository-Passwort sein?
Beliebig – Restic leitet per scrypt einen festen 512-Bit-Schlüssel ab. Ein zufälliger String mit 32 Zeichen ist ausreichend; mehr als 64 Zeichen bringt keinen Sicherheitsgewinn.
Ist das Repository portabel zwischen Backends?
Ja. restic -r <ziel> copy --from-repo <quelle> überträgt Snapshots von einem Repository in ein anderes. Das Format ist Backend-unabhängig.
Wie erkenne ich, ob das Backup heute gelaufen ist?
restic snapshots --latest 1 zeigt den neuesten Snapshot. Im systemd-Betrieb liefert journalctl -u restic-backup.service --since yesterday den vollständigen Lauf-Log. Für proaktive Überwachung lässt sich Restic mit einem curl-Aufruf an Healthchecks.io am Ende des ExecStart integrieren; für eine vollständig betreute Backup-Infrastruktur ist das ein typisches Thema des IT-Supports & Server-Betriebs.
Was passiert, wenn der Bucket gelöscht wird? Das Repository ist weg. Object-Locking (S3 Object Lock oder Backblaze B2 Object Lock) verhindert, dass Backups durch Ransomware oder einen Bedienfehler gelöscht werden können. Wie groß der finanzielle Schaden eines unerwarteten Datenverlustes für den Betrieb wäre, lässt sich mit dem Ausfallkostenrechner abschätzen.
Retention, Monitoring und Object-Lock
Sinnvolle nächste Schritte nach dem ersten laufenden Backup: restic stats zeigt Gesamtgröße und Deduplizierungsrate und hilft, die Retention-Policy auf das tatsächliche Datenvolumen abzustimmen. Für einen vollständigen CI-Prozess – automatischer Build, Test und anschließendes Datenbank-Backup als Artefakt – bietet sich das GitHub-Actions-Boilerplate für .NET und Docker als Ausgangspunkt an. Object-Lock aktivieren Sie direkt im Bucket-Management Ihres Anbieters; Restic selbst muss dafür nicht konfiguriert werden. Die vollständige Referenz für alle weiteren Backends (SFTP, REST, Rclone) und Spezialfälle wie restic mount finden Sie unter restic.readthedocs.io.
Hinweis: Die Beiträge dieses Blogs werden unter Einsatz von KI erstellt und vor der Veröffentlichung redaktionell geprüft. Die redaktionelle Verantwortung trägt Emre Yurtbay (siehe Impressum).