Skip to content

Instantly share code, notes, and snippets.

@rolfn
Last active May 1, 2024 13:49
Show Gist options
  • Star 19 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save rolfn/5637ba74fdc152ea38eaa99fa3fa1bda to your computer and use it in GitHub Desktop.
Save rolfn/5637ba74fdc152ea38eaa99fa3fa1bda to your computer and use it in GitHub Desktop.
Backup auf Cloud-Speicher mit »restic«

Automatische Backups auf Online-Speicher mit »Restic«

Das Programm »Restic« ist ein modernes Backup-Programm, welches als Speicherort sowohl lokale Verzeichnisse als auch per Netzwerk erreichbare Speicher (Online-Speicher) nutzen kann. Der Autor von »Restic« zeigt in anschaulicher Weise in zwei Videos viele Details zur Arbeitsweise seines Programms: »FOSDEM 2015« (2015-01-28) und »CCCCologne« (2016-01-29).

Im Folgenden soll gezeigt werden, wie man unter Linux automatische Backups mit »Restic« einrichten kann. Als Speicherort wird per WebDAV-Protokoll erreichbarer Online-Speicher genutzt. Sinngemäß können die Hinweise aber auch auf andere Netzwerkprotokolle übertragen werden. Getestet wurde unter »openSUSE« und »Linux Mint« (»Ubuntu«), wobei aber auch alle anderen systemd-basierten Linux-Distributionen in derselben Art oder mit geringfügigen Änderungen geeignet sind.

Installation der benötigten Software

Restic

openSUSE Ubuntu, Linux Mint
sudo zypper up sudo apt update
sudo zypper in restic fuse sudo apt install restic fuse

Ab Version »0.9.3« kann man »Restic« auch unabhängig vom Paketmanager der Linux-Distribution per

sudo restic self-update

auf den aktuellen Stand bringen.

Rclone

»Restic« unterstützt nicht selbst den Zugriff auf Online-Speicher per WebDAV-Protokoll, kann aber die Fähigkeiten des Programms »Rclone« dafür nutzen. »Rclone« bietet sehr einfach die Möglichkeit, auf eine Vielzahl von Online-Speicher zuzugreifen.

openSUSE Ubuntu, Linux Mint
sudo zypper in rclone sudo apt install rclone

Per

sudo rclone selfupdate

kann »Rclone« auch unabhängig vom Paketmanager der Linux-Distribution auf den aktuellen Stand gebracht werden.

E-Mail-Programm

Um regelmäßig über das Ergebnis der Backup-Läufe informiert zu werden, muss ein geeignetes Programm zum Verschicken von E-Mails installiert werden.

openSUSE Ubuntu, Linux Mint
sudo zypper in msmtp sudo apt install msmtp

WebDAV-Zugang

Dazu muss »Rclone« geeignet konfiguriert werden:

rclone config

e/n/d/r/c/s/q> n
name> GMX
Storage> 31
url> https://mediacenter.gmx.net
vendor> 4
user> Max.Mustermann@gmx.de
y/g/n> y
password: ???????????
Confirm the password: ???????????
bearer_token>
y/n> n
y/e/d> y
e/n/d/r/c/s/q> q

Die zu beantwortenden Fragen sind weitgehend selbsterklärend. Als Name wurde hier »GMX« gewählt; andere Namen sind ebenso möglich. Als Ergebnis der Rclone-Konfiguration entsteht die Konfigurationsdatei ~/.config/rclone/rclone.conf. Weitere allgemeine Hinweise zur Rclone-Nutzung finden sich hier.

Anlegen und Editieren diverser Konfigurationsdateien

  • ~/.config/backup/backup-env.txt

    RESTIC_REPOSITORY='rclone:GMX:backup'
    RESTIC_PASSWORD='???????????'
    RCLONE_TPSLIMIT=4
    MAIL_TO='Max.Mustermann@gmx.de'

    Erklärung: Die hier definierte Umgebungsvariable RESTIC_REPOSITORY legt fest, wie »Restic« das Verzeichnis zum Speichern der Backups erreichen kann. Es ist das Verzeichnis backup auf dem von »Rclone« erreichbaren Online-Speicher mit Namen »GMX«. RESTIC_PASSWORD legt das zum Ver- und Entschlüsseln zu benutzende Passwort fest. Dieses Passwort sollte möglichst kompliziert sein und zusätzlich an einem sicheren Ort aufbewahrt werden.

    Tests hatten ergeben, dass der Server des hier als Beispiel gwählten Online-Speichers gelegentlich Fehler der Art

    CLIENT_USER has exceeded the limit of 15000.0 requests per Hours for this client.:
    429 Too Many Requests
    

    meldete. Das Rclone-Flag --tmplimit=xxx lässt sich verwenden, um diesen Fehler zu unterdrücken. Die Umgebungsvariable RCLONE_TPSLIMIT ist äquivalent zu diesem Flag. Der Wert »4« ergibt sich aus 15000 / 1h = 15000 / 3600s ≈ 4/s.

    Mit

    chmod go-rw ~/.config/backup/backup-env.txt

    sollte die Konfigurationsdatei als nur für ihren Eigentümer lesbar gekennzeichnet werden.

  • ~/.config/backup/backup-include.txt

    /home/mustermann
    

    Erklärung: Angabe der Verzeichnisse (eins pro Zeile), die von »Restic« gesichert werden sollen. In diesem Beispiel wird als einzige Angabe das gesamte HOME-Verzeichnis ausgewählt.

  • ~/.config/backup/backup-exclude.txt

    /home/mustermann/.cache
    /home/mustermann/.mozilla
    /home/mustermann/.local/share/Trash
    /home/mustermann/.thunderbird
    /home/mustermann/Download
    /home/mustermann/tmp
    /home/mustermann/VirtualBox VMs
    /home/mustermann/logs
    

    Erklärung: Beispielhafte Angabe der Verzeichnisse (eins pro Zeile), die von »Restic« von der Sicherung ausgenommen werden sollen. Es ist zu beachten, dass Quotes anders als beim Kommando-Interpreter relevant sind, hier also im Allgemeinen vermieden werden müssen.

  • ~/.config/systemd/user/backup.timer

    [Unit]
    Description=Backup (Timer)
    
    [Timer]
    OnCalendar=19:30
    Persistent=false
    
    [Install]
    WantedBy=default.target

    Erklärung: Es wird angenommen, dass einmal täglich von »Restic« ein Backup-Lauf gestartet werden soll. OnCalendar bestimmt den Zeitpunkt (siehe auch: »Time and date specifications«). Die Angabe Persistent=false sorgt dafür, dass ein versäumter Aufruf von backup.service nach Wiedereinschalten des Rechners nicht nachgeholt wird.

  • ~/.config/systemd/user/backup.service

    [Unit]
    Description=Backup (%H)
    Documentation=https://restic.readthedocs.io/en/latest/
    
    [Service]
    Type=oneshot
    EnvironmentFile=-/etc/environment
    EnvironmentFile=%h/.config/backup/backup-env.txt
    ExecStartPre=/bin/bash -lc "restic --no-lock unlock --remove-all"
    ExecStartPre=/usr/bin/systemd-cat -- echo '*** RESTIC ***'
    ExecStart=/bin/bash -lc "restic backup \
      --files-from   %h/.config/backup/backup-include.txt \
      --exclude-file %h/.config/backup/backup-exclude.txt \
      --one-file-system --exclude-caches || ( e=$?; [ $e -eq 3 ] || exit $e )"
    # ignore "error 3" (some source data could not be read)
    ExecStopPost=/bin/bash -lc "restic --no-lock unlock --remove-all"
    #
    ExecStopPost=/usr/bin/systemd-cat -- echo '*** EMAIL ***'
    ExecStopPost=/bin/bash -lc '%h/.config/systemd/user/backup-email.sh "%N"'
    
    [Install]
    WantedBy=default.target
    

    Erklärung: backup.service beinhaltet die Abläufe, die von backup.timer zeitgesteuert gestartet werden. Mit Nice=15 wird deren Priorität auf einen unterdurchschnittlichen Wert gesenkt (siehe auch: man nice). Der eigentliche Backup-Aufruf geschieht in ExecStart. In ExecStopPost wird die Benachrichtigungs-E-Mail versendet.

  • ~/.config/systemd/user/backup-forget-prune.timer

    [Unit]
    Description=Purge backup snapshots (Timer)
    
    [Timer]
    OnCalendar=Mon 20:00:00 
    Persistent=false  
    
    [Install]
    WantedBy=default.target

    Erklärung: Es wird angenommen, dass einmal wöchentlich die von »Restic« gespeicherten Backups bereinigt werden sollen. Die Angabe Persistent=false sorgt dafür, dass ein versäumter Aufruf von backup-forget-prune.service nach Wiedereinschalten des Rechners nicht nachgeholt wird.

  • ~/.config/systemd/user/backup-forget-prune.service

    [Unit]
    Description=Forget/Prune/Check snapshots (%H)
    Documentation=https://restic.readthedocs.io/en/latest/
    
    [Service]
    Type=oneshot
    Nice=15
    EnvironmentFile=-/etc/environment
    EnvironmentFile=%h/.config/backup/backup-env.txt
    ExecStartPre=/bin/bash -lc "restic --no-lock unlock --remove-all"
    ExecStartPre=/usr/bin/systemd-cat -- echo '*** RESTIC ***'
    ExecStart=/bin/bash -lc "restic forget \
      --keep-daily 7 --keep-weekly 4 --keep-monthly 12"
    ExecStart=/bin/bash -lc "restic prune --max-unused 0"
    ExecStart=/bin/bash -lc "restic unlock --remove-all"
    ExecStart=/bin/bash -lc "restic --no-lock check --cleanup-cache"
    ExecStopPost=/bin/bash -lc "restic --no-lock unlock --remove-all"
    # if supported
    ExecStopPost=/bin/bash -lc "rclone cleanup $REMOTE_REPOSITORY"
    ExecStopPost=/usr/bin/systemd-cat -- echo '*** EMAIL ***'
    ExecStopPost=/bin/bash -lc '%h/.config/systemd/user/backup-forget-prune-email.sh "%N"'
    
    [Install]
    WantedBy=default.target

    Erklärung: backup-forget-prune.service beinhaltet die Abläufe, die von backup-forget-prune.timer zeitgesteuert gestartet werden. Es werden ältere Backups nach einer sinnvollen Strategie gelöscht. Die Angaben --keep-daily 7, --keep-weekly 4 und --keep-monthly 12 haben folgende Bedeutung: Hebe monatlich ein Backup 12 Monate rückwirkend auf und hebe wöchentlich ein Backup 4 Wochen rückwirkend auf und hebe täglich ein Backup 7 Tage rückwirkend auf.

  • ~/.msmtprc

    defaults
    
    port 465
    # Always use TLS.
    tls on
    tls_starttls off
    set_from_header on
    
    account gmx
    host mail.gmx.de
    
    # Envelope-from address
    from Mustermann@gmx.de
    
    add_missing_date_header on
    add_missing_from_header on
    
    auth on
    user Mustermann@gmx.de
    password "???????????????"
    
    account default: gmx

    Erklärung: Enthält Angaben zu einem beispielhaften E-Mail-Account (E-Mail-Provider »GMX«). Das E-Mail-Programm msmtp greift auf diese Informationen zu.

  • ~/.config/systemd/user/backup-email.sh

    #!/bin/bash
    
    # $1 = Unit (short)
    
    if [ -z "${MAIL_TO}" ]; then exit 0; fi
    
    HOST=$(hostname)
    
    SNAPSHOTS=$(restic snapshots --host $HOST --compact 2>/dev/null)
    LAST=$(echo "$SNAPSHOTS" | tail -3 | head -1 | awk '{print $1}')
    PREV=$(echo "$SNAPSHOTS" | tail -4 | head -1 | awk '{print $1}')
    if [[ ${PREV:0:1} == '-' ]]; then PREV=${LAST}; fi
    
    InvocationID=$(systemctl show --property=InvocationID --value --user $1.service)
    MESSAGE1=$(journalctl --quiet _SYSTEMD_INVOCATION_ID="$InvocationID")
    MESSAGE2=$(restic diff "$PREV" "$LAST" 2>/dev/null)
    
    if [ "$SERVICE_RESULT" == "success" ]; then
      SUBJECT="Success: $1 ($HOST)"
    elif [ "$SERVICE_RESULT" == "exit-code" ]; then
      SUBJECT="Error $EXIT_STATUS: $1 ($HOST)"
    else
      SUBJECT="$SERVICE_RESULT: $1 ($HOST)"
    fi
    
    cat <<EOF | msmtp -t > /dev/null \
    
    To: $MAIL_TO
    Subject: $SUBJECT
    Content-Type: text/plain; charset="utf-8"
    Content-Transfer-Encoding: Quoted-Printable
    Content-Encoding: UTF8
    MIME-Version: 1.0
    
    $MESSAGE1
    
    $MESSAGE2
    EOF
    
    exit 0

    Das Skript muss ausführbar gemacht werden:

    chmod ugo+x  ~/.config/systemd/user/backup-email.sh

    Erklärung: Wird von backup.service genutzt, um im Erfolgsfall wie auch im Fehlerfall eine E-Mail mit einem Bericht zu dem Systemd-Aufruf zu verschicken.

  • ~/.config/systemd/user/backup-forget-prune-email.sh

    #!/bin/bash
    
    # $1 = Unit (short)
    
    HOST=$(hostname)
    
    InvocationID=$(systemctl show --property=InvocationID --value --user $1.service)
    MESSAGE=$(journalctl --quiet _SYSTEMD_INVOCATION_ID="$InvocationID")
    
    if [ "$SERVICE_RESULT" == "success" ]; then
      SUBJECT="Success: $1 ($HOST)"
    elif [ "$SERVICE_RESULT" == "exit-code" ]; then
      SUBJECT="Error $EXIT_STATUS: $1 ($HOST)"
    else
      SUBJECT="$SERVICE_RESULT: $1 ($HOST)"
    fi
    
    cat <<EOF | msmtp -t > /dev/null \
    
    To: $MAIL_TO
    Subject: $SUBJECT
    Content-Type: text/plain; charset="utf-8"
    Content-Transfer-Encoding: Quoted-Printable
    Content-Encoding: UTF8
    MIME-Version: 1.0
    
    $MESSAGE
    EOF
    
    exit 0

    Das Skript muss ausführbar gemacht werden:

    chmod ugo+x  ~/.config/systemd/user/backup-forget-prune-email.sh

    Erklärung: Wird von backup-forget-prune.service genutzt, um im Erfolgsfall wie auch im Fehlerfall eine E-Mail mit einem Bericht zu dem Systemd-Aufruf zu verschicken.

    Mit

    ( set -a ; source ~/.config/backup/backup-env.txt; \
      ~/.config/systemd/user/backup-email.sh backup )
    
    ( set -a ; source ~/.config/backup/backup-env.txt; \
      ~/.config/systemd/user/backup-forget-prune-email.sh backup-forget-prune )

    lässt sich das Funktionieren der E-Mail-Benachrichtigungen testen. Misslingt das E-Mail-Verschicken, müssen ggf. die Angaben in ~/.msmtprc angepasst werden. Soll das Verschicken von E-Mails verhindert werden, muss in ~/.config/backup/backup-env.txt der Variablen MAIL_TO ein Leerstring zugewiesen oder die Zeile ganz gelöscht werden.

Ort der künftigen Backups im Web-Speicher anlegen

rclone mkdir GMX:backup

Initialisierung der Backup-Struktur

Der folgende Aufruf

( set -a ; source ~/.config/backup/backup-env.txt; restic init )

ist einmalig nötig, um die Dateistruktur zur Aufnahme der Backups anzulegen.

Starten der nötigen Prozesse

Die folgenden Aufrufe starten die Systemd-Timer und legen fest, dass sie nach jedem Einloggen automatisch gestartet werden:

systemctl --user enable backup.timer
systemctl --user start  backup.timer
systemctl --user enable backup-forget-prune.timer
systemctl --user start  backup-forget-prune.timer

Mit

systemctl --user list-timers  

kann überprüft werden, ob und wann die neu gestarteten Timer ihre nächste Aktion ausführen werden.

Unabhängig von dem automatischen Ablauf, kann ein einzelner Backup-Lauf folgendermaßen gestartet werden:

systemctl --user start backup.service

Dies empfiehlt sich insbesondere beim ersten Backup, der naturgemäß relativ lange dauert. In einem zweiten Terminal-Fenster kann per

journalctl -f --user-unit backup.service

der Ablauf beobachtet werden. Ein erfolgreicher Backup-Lauf beginnt mit »Starting Backup« und endet mit »Finished Backup«. Die Hinweise

ERROR : index: error listing: directory not found
ERROR : snapshots: error listing: directory not found

beim ersten Backup-Lauf können ignoriert werden. Die Ursache ist die noch unvollständige Datei-Struktur unterhalb des Zielverzeichnisses.

Liste der bereits durchgeführten Backups

Der Aufruf

( set -a ; source ~/.config/backup/backup-env.txt; restic snapshots --no-lock --compact )

zeigt eine Liste der Einzel-Backups (auch »snapshots« genannt).

Einbinden der entschlüsselten Backups ins Dateisystem

Mit Anlegen der Datei ~/.config/systemd/user/backup-mount.service folgenden Inhalts

[Unit]
Description=Mounting the backup
Documentation=https://restic.readthedocs.io/en/latest/

[Service]
Type=simple
Environment=mp="%h/mnt/backup"
EnvironmentFile=%h/.config/backup/backup-env.txt
ExecStartPre=/bin/bash -c 'mkdir -p "$mp"'
ExecStart=/bin/bash -lc 'if $(mountpoint -q "$mp"); then true; else \
  restic mount "$mp"; fi'
ExecStop=/bin/bash -c 'if ! $(mountpoint -q "$mp"); then true; else \
  fusermount -zu "$mp"; fi'

[Install]
WantedBy=default.target

und durch die Aufrufe

systemctl --user enable backup-mount.service
systemctl --user start  backup-mount.service

werden die entschlüsselten Backups über ~/mnt/backup sofort und nach jedem neuen Einloggen automatisch zugänglich. Unabhängig davon kann der Mount-Prozess per

systemctl --user stop  backup-mount.service

beendet werden.

Wiederherstellen des gesamten Inhalts eines Backups

Sollen nur einzelne Dateien aus dem letzten oder einem früheren Backup wiederhergestellt werden, ist der Zugang über ~/mnt/backup gut geeignet. Will man dagegen den gesamten Inhalt wiederherstellen, weil z.B. die Festplatte mit dem HOME-Verzeichnis defekt ist, geht man folgendermaßen vor:

( set -a ; source ~/.config/backup/backup-env.txt; \
  restic restore latest --target )

Es wird davon ausgegangen, dass das HOME-Verzeichnis auf der neuen Festplatten nahezu leer neu angelegt wurde und komplett durch die im zuletzt erzeugten Backup gespeicherten Daten ersetzt werden soll. Der gezeigte Aufruf setzt natürlich voraus, dass die genutzte Datei backup-env.txt bereits vorhanden ist. Es ist daher nötig, sie oder besser gleich das gesamte Verzeichnis ~/.config unabhängig von den Backups an einem sicheren Ort zu speichern und vor dem Restore-Aufruf wiederherzustellen.


Soll, anders als hier gezeigt, die System-Installation des Rechners als Backups gesichert werden, müssen die gezeigten Abläufe mit Root-Rechten gestartet werden, d.h. --user muss bei den systemctl-Aufrufen entfallen und die Systemd-Dateien müssen an andere Plätze kopiert werden.

Weitergehende Hinweise zu »Restic« findet man hier.


Ein Artikel im »Fedora Magazine« unter dem Titel »Automate backups with restic and systemd« behandelt das hier dargestellte Thema in sehr ähnlicher Weise.

Rolf Niepraschk (Neufassung, 12/2023 ...)

@tomthecat
Copy link

Sehr hilfreich, habe damit mein Backup aufgesetzt. Danke!

Zwei Hinweise:

  1. mailx führt ab v15 einen neuen Stil für die Credentials etc. ein, die ~/.mailrc sollte daher wie folgt ausschauen:
account "backup" {
  set v15-compat
  localopts yes
  set smtp-auth=login
  set smtp-use-starttls
  set from="Backup Daemon <USERNAME@gmx.de>"
  set mta=smtps://USERNAME%40gmx.de:PASSWORD@mail.gmx.de:465
}
  1. Um das Funktionieren der Email-Benachrichtigungen zu testen, müssten die Befehle wie folgt lauten:
eval $(cat $HOME/.config/backup/backup-env.txt) \
  ~/.config/systemd/user/backup-email.sh backup

eval $(cat $HOME/.config/backup/backup-env.txt) \
  ~/.config/systemd/user/backup-forget-prune-email.sh backup-forget-prune

@rolfn
Copy link
Author

rolfn commented Nov 25, 2023

Vielen Dank für die Hinweise.

  • Zu .mailrc: Ich habe den Text oben korrigiert. Ich selbst verwende allerdings jetzt statt mailx msmtp, wobei mir der Grund der Änderung gar nicht mehr einfällt. Vielleicht war es wegen der besseren Verfügbarkeit auf unterschiedlichen Linux-Systemen. Zusätzlich habe ich auch noch die beiden E-Mail-Skripte angepasst. Beispielsweise wird jetzt journalctl statt systemctl --user status verwendet und sichergestellt, dass sämtliche Informationen des betreffenden Systemd-Laufes ausgegeben werden (wichtig, wenn sich das Backup über Mitternacht hinzieht).

  • Die Tests zu den E-Mail-Skripten sind tatsächlich falsch. Allerdings funktionieren die von Dir korrigierten Aufrufe bei mir auch nicht, obwohl sie es früher taten. Keine Ahnung, was da los ist. Mittlerweile verwende ich dafür und für die anderen Tests nicht mehr eval sondern mache es robuster so:

    ( set -a; source ~/.config/backup/backup-env.txt;
      ~/.config/systemd/user/backup-email.sh backup )

    Vorerst habe ich den Text oben, so wie von Dir angegeben, korrigiert.

Ich müsste eigentlich den ganzen Text auf den neuesten Stand bringen. Mal sehen.

Rolf

@rolfn
Copy link
Author

rolfn commented Dec 5, 2023

Ich habe den Inhalt in großen Teilen erneuert.

@cmonty14
Copy link

Hinweis:
resticprofile schließt die Lücke zur Erstellung einer restic Konfiguration inkl. Scheduling.
Dies ist auch bei restic dokumentiert.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment