Skip to content

Server Backups

Verschlüsselte, deduplizierende, inkrementelle Backups mit restic. Funktioniert mit S3, SFTP, Hetzner Storage Box, lokaler Platte, Backblaze B2. Retention wird server-seitig durchgesetzt, nicht durch Cron-Logs.

Quick-Links: Server erstinstallation Phase 9 · Server härten


Kriterium tar/rsync restic
Verschlüsselung at-rest ❌ selbst basteln ✅ AES-256, Repo selbst ist Krypto-blob
Deduplizierung ❌ jedes Backup Vollkopie ✅ Content-defined Chunking
Inkrementelle Snapshots manuell (rsync –link-dest) ✅ nativ
Retention (“behalte letzte 7, 4 weekly, 6 monthly”) Cron + Logik selbst forget --keep-*
Restore in ein beliebiges Verzeichnis
Tooling-Komplexität niedrig mittel
Doku & Community ✅ sehr aktiv

Für private Setups ist restic fast immer die richtige Wahl. borg ist die Alternative mit ähnlichem Funktionsumfang — wenn du Borg kennst, bleib dabei.


Ziel Kosten Geschwindigkeit Empfehlung
Hetzner Storage Box ~3.50 €/Monat (1 TB) Gigabit ✅ Standard für EU-Server
Backblaze B2 $0.005/GB/Monat schnell ✅ wenn >5 TB oder US-nähe
S3 (AWS, Hetzner, Contabo) variiert schnell ✅ in Stack integriert
Zweite Platte / NFS lokal kostenlos sehr schnell ⚠️ zählt nicht als “off-site” — Brand/RAID-Ausfall = weg
rsync.net variiert mittel ❌ overkill für Privat

Für diese Anleitung: SFTP-Backend (Hetzner Storage Box, oder beliebiger SSH-Server).


Phase 1: Installation + Repo initialisieren

Section titled “Phase 1: Installation + Repo initialisieren”
Terminal window
sudo apt install restic
restic version
# restic 0.16.x oder neuer

Repository-Initialisierung (einmalig pro Server × Ziel-Kombination):

Terminal window
# Storage-Box oder SFTP-Server als Ziel
export RESTIC_REPOSITORY="sftp:backup@backup.example.com:/srv/backups/server-01"
export RESTIC_PASSWORD_FILE="/root/.config/restic/passphrase"
# Passphrase generieren, sicher ablegen (auf dem Server, NICHT im Repo!)
mkdir -p /root/.config/restic
chmod 700 /root/.config/restic
openssl rand -hex 32 | sudo tee /root/.config/restic/passphrase
chmod 600 /root/.config/restic/passphrase
# Repo initialisieren
restic init
# "created restic repository ... at sftp:..."
# "Please note that knowledge of your password is required ..."

⚠️ Die Passphrase ist der einzige Schlüssel. Verlierst du sie, sind die Backups mathematisch unknackbar. Backup der Passphrase in einem Passwort-Manager (1Password/Bitwarden/KeePass) ablegen.

Terminal window
# Was soll gesichert werden?
restic backup /etc /home /var/log /root /opt /var/www
# Schnell prüfen, was drin ist
restic snapshots
# ID Time Host Tags
# --------------------------------------------------------------
# a1b2c3d4 2026-07-02 03:00:00 server-01

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

#!/usr/bin/env bash
set -euo pipefail
export RESTIC_REPOSITORY="sftp:backup@backup.example.com:/srv/backups/server-01"
export RESTIC_PASSWORD_FILE="/root/.config/restic/passphrase"
# Was sichern? Server-spezifisch anpassen
PATHS="/etc /home /var/log /root /opt /var/www"
# Retention: 7 täglich, 4 wöchentlich, 6 monatlich
restic backup $PATHS
restic forget \
--keep-daily 7 \
--keep-weekly 4 \
--keep-monthly 6 \
--prune
# Healthcheck: Repo-Konsistenz
restic check
# Mail bei Fehler (optional, braucht mailutils + SMTP)
if [ $? -ne 0 ]; then
echo "Backup-Fehler auf $(hostname)" | mail -s "BACKUP FAIL $(hostname)" admin@example.com
fi
Terminal window
sudo chmod 700 /usr/local/bin/backup-restic.sh
# Cron: täglich 03:00
sudo crontab -e
# 0 3 * * * /usr/local/bin/backup-restic.sh >> /var/log/restic-backup.log 2>&1

Ein Backup, das nie restored wurde, ist kein Backup. Teste vor dem ersten produktiven Einsatz, und alle 3-6 Monate.

Terminal window
# In Test-Verzeichnis restoren (überschreibt nichts im Live-System)
restic restore latest --target /tmp/restore-test
ls /tmp/restore-test/etc
ls /tmp/restore-test/home
# Verzeichnisse da? Inhalt plausibel?
# Einzelne Datei rausziehen (häufigster Use-Case)
restic restore latest --target /tmp/restore-test \
--include /etc/nginx/nginx.conf
cat /tmp/restore-test/etc/nginx/nginx.conf
# Bestimmtes Verzeichnis
restic restore latest --target /tmp/restore-test \
--include /var/www

Restore auf einen anderen Server (Disaster-Recovery-Szenario):

Terminal window
# Auf neuem Server:
apt install restic
# Passphrase + Repo-URL konfigurieren (aus Passwort-Manager kopieren)
restic -r sftp:backup@backup.example.com:/srv/backups/server-01 snapshots
restic -r sftp:backup@backup.example.com:/srv/backups/server-01 restore latest \
--target /restore
Terminal window
# /usr/local/bin/backup-restic.sh erweitern:
EXIT_CODE=$?
if [ $EXIT_CODE -ne 0 ]; then
# systemd-notify, Mail, Telegram, etc.
curl -s -X POST https://api.telegram.org/bot<TOKEN>/sendMessage \
-d chat_id=<CHAT_ID> \
-d text="🚨 Backup failed on $(hostname): exit $EXIT_CODE"
fi

Cron: wöchentlich restic check einplanen (Repo-Konsistenz).

Terminal window
# In backup-restic.sh oder separater Cronjob:
restic check
# liest alle Pack-Files und validiert Struktur
Terminal window
LAST=$(restic snapshots --json | jq -r '.[-1].time')
AGE_HOURS=$(( ( $(date +%s) - $(date -d "$LAST" +%s) ) / 3600 ))
[ $AGE_HOURS -gt 26 ] && echo "ALARM: letztes Backup älter als 26h"

restic forget löscht Snapshots gemäß Policy, restic prune räumt ungenutzte Daten weg (langsam, speicherintensiv).

Beispiel-Kurven:

--keep- Beispiel Effekt
daily 7 7 tägliche Snapshots letztes Backup + 6 Tage davor
weekly 4 4 wöchentliche letzte Woche + 3 davor
monthly 6 6 monatliche Halbes Jahr Rückblick
yearly 10 10 jährliche für Compliance

Reihenfolge zählt: --keep-daily zuerst, dann --keep-weekly (die “ältesten” wöchentlichen, die noch keine Daily-Vertretung haben), etc.

  • /etc — Konfiguration (oft vergessen!)
  • /home — User-Daten
  • /var/www oder App-Verzeichnisse
  • /opt — selbst-installierte Software
  • /var/log — Logs (für Post-Mortem nach Crash)
  • /root — root’s Home (Scripts, SSH-Config)
  • /proc, /sys, /dev, /tmp, /run — Pseudo-Dateisysteme
  • /var/cache — kannst du neu erzeugen
  • Großer Müll (/var/lib/docker/overlay2, /var/lib/libvirt) — Repo-Killer, separat behandeln
Terminal window
restic backup /var/www \
--exclude='*.log' \
--exclude='node_modules' \
--exclude='.cache'
Terminal window
# Snapshot vor Migration
restic backup /etc /home /var/log /root /opt /var/www
restic snapshots
# → letzte Snapshot-ID notieren
# Server migrieren
# Auf neuem Server: restore + Repo initialisiert lassen

Repository-Migration (z.B. Storage-Box-Anbieter-Wechsel)

Section titled “Repository-Migration (z.B. Storage-Box-Anbieter-Wechsel)”
Terminal window
# Repo A: SFTP alt
export RESTIC_REPOSITORY="sftp:backup@old.example.com:/srv/backups"
export RESTIC_PASSWORD_FILE="/root/.config/restic/passphrase"
restic snapshots
# Repo B: B2 neu initialisieren
export RESTIC_REPOSITORY="b2:bucket-name:server-01"
export B2_ACCOUNT_ID="..."
export B2_ACCOUNT_KEY="..."
restic init
# Snapshots von A nach B kopieren (ohne neuen Backup-Run)
restic copy --repo2 sftp:backup@old.example.com:/srv/backups
# ODER: restic migrate (nur wenn Repo-Format-Versionen abweichen)

Problem Ursache Fix
Fatal: unable to open config file falsche Repo-URL oder kein Zugriff restic snapshots mit expliziten ENV-Variablen testen
Fatal: parse error Repo-Format-Version mismatch restic migrate ausführen
repository is locked vorheriger Run nicht sauber beendet restic unlock
Snapshot dauert ewig erstes Backup, viel Daten --one-file-system + --exclude-caches nutzen, Repo-Größe überwachen
Repo wächst trotz forget prune vergessen immer forget --prune zusammen
check findet Fehler Bitrot / Netzwerk-Issue restic repair snapshots + restic repair packs (nicht ohne Backup-Passphrase)

  1. Panik bleibt aus. Du hast die Passphrase im Passwort-Manager.
  2. Neuen Server provisionieren (gleicher Cloud-Provider oder anderer).
  3. Restic installieren, Passphrase + Repo-URL setzen.
  4. restic snapshots → aktuellste Snapshot-ID wählen.
  5. restic restore latest --target / oder nur die kritischen Pfade.
  6. Dienste neu starten (Docker, systemd, was auch immer).
  7. Backups auf neuem Server neu einrichten (gleiche Cron-Jobs).
  8. Post-Mortem: was war kaputt, warum hat es nicht früher angekündigt?