Server Backup via Borg auf Storage Box
Mein Root Server läuft jetzt schon seit mehreren Jahren durch und bislang hatte ich noch keinen Need irgendwie meine Daten zu sichern, da die Projekte eh in Repos liegen und meine Daten auch immer mit meinem lokalen Netzwerk gesynct wird. Heute wollte ich es mir aber einfacher machen, die Daten des Root Servers auch Online auf einer Storage Box zu backuppen und mehrere Versionen zur Verfügung zu haben. Dazu habe ich mir entschlossen bei Hetzner eine Storage Box zu mieten und meinen Server mit Borg dorthin zu sichern.
Borg ist für deduplizierte, verschlüsselte, inkrementelle Backups auf SSH-Ziele optimiert und wird von Hetzner explizit unterstützt. Vorteile: starke Deduplizierung, integrierte Verschlüsselung (AES-256/ChaCha20-Poly1305), einfache Retention und Mountbarkeit als FUSE-FS zum Restore einzelner Dateien.
apt install borgbackup
SSH Key für die Storage Box generieren (ohne Passwort!).
ssh-keygen -t ed25519 -C "borg@rootserver" -f ~/.ssh/hetzner_storage
Den Key kann man beim Erstellen der Storage Box hinzufügen oder wenn schon die Box eingerichtet ist, nachträglich hinzufügen.
ssh-copy-id -p23 -i ~/.ssh/hetzner_storage.pub uXXXXX@uXXXXX.your-storagebox.de
Borg Repo initialisieren (REPO_NAME ersetzen, bei mir z.B. nextcloud-docker, für mein Nextcloud Backup).
borg init --encryption=repokey --remote-path=borg1.4 ssh://uXXXXX@uXXXXX.your-storagebox.de:23/./REPO_NAME -i ~/.ssh/hetzner_storage
Meine Nextcloud läuft in Docker und die Daten sind nur per Mount eingebunden. Die Container selbst möchte ich nicht sichern, nur die Daten und die Datenbank. Dazu habe ich mir ein Script erstellt, was mir die DB per Docker und mysqldump dumped und die Daten sichert. Mein Backup soll verschlüsselt in der Storage Box liegen, dazu habe ich den Schlüssel in der Datei /root/.borg-passphrase abgelegt.
#!/bin/bash
set -euo pipefail
# === KONFIG ===
export BORG_RSH='ssh -i /root/.ssh/hetzner_borg -p 23 -o StrictHostKeyChecking=no'
export BORG_PASSPHRASE=$(cat /root/.borg-passphrase)
export BORG_REMOTE_PATH="borg-1.4"
NEXTCLOUD_CLI="nextcloud_cli"
NC_DATA="/mnt/docker/data/nextcloud/data"
NC_RELEASES="/mnt/docker/data/nextcloud/releases"
NC_OCC_PATH_IN_DOCKER="/var/www/nextcloud/releases/current/occ"
DB_CONTAINER="nextcloud_db"
DB_NAME="nextcloud"
DB_USER="root"
BORG_REPO="ssh://uXXXXX@uXXXXX.your-storagebox.de:23/./nextcloud-docker"
TMP_BACKUP="/tmp/nextcloud-$(date +%Y%m%d-%H%M%S)"
LOCK_FILE="/var/run/borg-nextcloud.lock"
LOG_FILE="/var/log/borg-nextcloud.log"
[ -f "$LOCK_FILE" ] && { echo "Backup läuft!" >&2; exit 1; }
echo $$ > "$LOCK_FILE"
trap 'rm -f "$LOCK_FILE" && docker exec $NEXTCLOUD_CLI php $NC_OCC_PATH_IN_DOCKER maintenance:mode --off || true; rm -rf "$TMP_BACKUP"' EXIT INT TERM
exec > >(tee -a "$LOG_FILE") 2>&1
echo "=== Nextcloud Backup start: $(date) ==="
# 1. Maintenance ON
docker exec $NEXTCLOUD_CLI php $NC_OCC_PATH_IN_DOCKER maintenance:mode --on
# 2. DB Dump (mit Passwort!)
mkdir -p "$TMP_BACKUP/sql"
DB_PASS=$(docker exec $DB_CONTAINER printenv MYSQL_ROOT_PASSWORD)
docker exec $DB_CONTAINER mysqldump --single-transaction --routines --triggers \
--user="$DB_USER" --password="$DB_PASS" "$DB_NAME" | gzip > "$TMP_BACKUP/sql/db.sql.gz"
# 3. Config backup
docker exec $NEXTCLOUD_CLI php $NC_OCC_PATH_IN_DOCKER config:list --output=json > "$TMP_BACKUP/config.json"
# 4. Borg Create
borg create "$BORG_REPO"::nextcloud-{now:%Y-%m-%d-%H:%M} \
"$TMP_BACKUP/" \
"$NC_DATA" \
"$NC_RELEASES" \
--exclude "*/cache/*" \
--exclude "*/tmp/*" \
--exclude "*/updater-*" \
--exclude "*/appdata_*/*cache*" \
--compression lz4 --stats --progress
# 5. Prune
borg prune "$BORG_REPO" --stats \
--keep-within=1w \
--keep-weekly=4 \
--keep-monthly=6 \
--keep-yearly=2
echo "✅ Backup fertig: $(date)"
Meine Backups sollen jede Nacht um 3 Uhr laufen. Dazu habe ich mir einen Service und Timer angelegt.
Service: /etc/systemd/system/borg-nextcloud.service
[Unit]
Description=Nextcloud Borg Backup
After=network-online.target
[Service]
Type=oneshot
User=root
ExecStart=/root/nextcloud_backup.sh
Timer: /etc/systemd/system/borg-nextcloud.timer
[Unit]
Description=Täglich Nextcloud Backup
Requires=borg-nextcloud.service
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
[Install]
WantedBy=timers.target
Service und Timer aktivieren
systemctl daemon-reload
systemctl enable --now borg-nextcloud.timer
systemctl status borg-nextcloud.timer
Kleiner Test ob alles funktioniert (Achtung, je nach Menge an Daten kann dies ein paar Stunden dauern)
systemctl start borg-nextcloud.service