Skip to content

Instantly share code, notes, and snippets.

@rolfn
Last active December 22, 2024 22:03
Show Gist options
  • 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. Dem gezielten Zugriff auf Informationen im json-Datenformat dient jq.

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

Anlegen und Editieren diverser Konfigurationsdateien

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

    RCLONE_CONFIG_MYGMX_TYPE=webdav
    RCLONE_CONFIG_MYGMX_URL="https://mediacenter.gmx.net" 
    RCLONE_CONFIG_MYGMX_VENDOR=other
    RCLONE_CONFIG_MYGMX_USER='[email protected]'
    RCLONE_CONFIG_MYGMX_PASS='????????????????????????????????????????'
    RCLONE_TPSLIMIT=4
    RCLONE_TPSLIMIT_BURST=10
    RCLONE_RETRIES=100
    RCLONE_LOW_LEVEL_RETRIES=100
    RESTIC_REPOSITORY='rclone:MyGMX:backup'
    RESTIC_PASSWORD='??????????????'
    RESTIC_COMPRESSION=max
    MAIL_TO='[email protected]'

    Erklärung: Die mit RCLONE_CONFIG_ beginnenden Variablen definieren den Namen (»MyGMX«) sowie Art und Eigenschaften des per »Rclone« bereit gestellten Online-Speichers. Das für RCLONE_CONFIG_MYGMX_PASS angegebene Passwort ist die von »Rclone« benötigte »getarnte« Version des originalen Passworts, also ein Ergebnis des Aufrufs rclone obscure ORIGINALPASSWORT. Man könnte statt der RCLONE_CONFIG_-Variablen auch auf Definitionen aus der Rclone-Konfigurationsdatei (~/.config/rclone/rclone.conf) zurückgreifen. So ergibt sich aber der Vorteil, sämtliche Konfigurationsangaben in einer einzigen Datei zusammenfassen zu können. Vorheriges Konfigurieren von »Rclone« könnte so völlig entfallen, wenn es nicht für andere Zwecke benötigt wird. Einige Netzwerkprotokolle (z.B. S3) werden von »Restic« direkt unterstützt und würden keine Rclone-Definitionen benötigen. Weitere Hinweise zur allgemeinen Rclone-Nutzung finden sich hier.

    Die Variable RESTIC_REPOSITORY legt fest, wie »Restic« den Ort zum Speichern der Backups erreichen kann. Es handelt sich um das Verzeichnis backup auf dem per »Rclone« erreichbaren Online-Speicher mit Namen »MyGMX«, wie er gerade definiert wurde. RESTIC_PASSWORD legt das zum Ver- und Entschlüsseln der Backups zu benutzende Passwort fest. Dieses Passwort sollte möglichst kompliziert sein und zusätzlich an einem sicheren Ort aufbewahrt werden.

    Tests ergaben, dass der WebDAV-Server des hier als Beispiel gewä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 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. Die auf RETRIES endenden Angaben dienen ebenfalls der besseren Stabilität der Übertragung.

    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 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=timers.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, d.h. der nächste Backup-Lauf findet erst zum nächsten regulären Zeitpunkt statt.

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

    [Unit]
    Description=Restic backup -- service (%H)
    Documentation=https://restic.readthedocs.io/en/latest/
    
    [Service]
    Type=oneshot
    Nice=15
    IOSchedulingClass=best-effort
    IOSchedulingPriority=7
    EnvironmentFile=-/etc/environment
    EnvironmentFile=%E/backup/backup-env.txt
    #
    ExecStartPre=/bin/bash -lc "restic --no-lock unlock --remove-all --cleanup-cache"
    #
    ExecStart=/usr/bin/systemd-cat -- echo '*** RESTIC BACKUP (begin) ***'
    ExecStart=/bin/bash -lc "restic backup \
      --files-from   %E/backup/backup-include.txt \
      --exclude-file %E/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)
    ExecStart=/usr/bin/systemd-cat -- echo '*** RESTIC BACKUP (end) ***'
    #
    ExecStopPost=/bin/bash -lc "restic --no-lock unlock --remove-all --cleanup-cache"
    #
    ExecStopPost=/usr/bin/systemd-cat -- echo '*** EMAIL (begin) ***'
    ExecStopPost=/bin/bash -lc 'backup-email.sh "%N"'
    ExecStopPost=/usr/bin/systemd-cat -- echo '*** EMAIL (end) ***'

Erklärung: backup.service beinhaltet die Abläufe, die von backup.timer zeitgesteuert gestartet werden. Mit Nice=15 und IOSchedulingClass/IOSchedulingPriority werden die Prozessprioritäten auf unterdurchschnittliche Werte gesenkt (siehe auch: man systemd.exec). Der eigentliche Backup-Aufruf geschieht in ExecStart. In ExecStopPost wird eine Benachrichtigungs-E-Mail versendet. backup.service enthält, anders als viele der sonstigen .service-Dateien, keinen [Install]-Abschnitt, womit gewährleistet ist, dass ausschließlich die Angaben in backup.timer für den Prozess-Start verantwortlich sind.

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

    [Unit]
    Description=Restic backup (clean up snapshots) -- timer (%H)
    Documentation=https://restic.readthedocs.io/en/latest/
    
    [Timer]
    OnCalendar=Mon *-*-* 19:30:00
    Persistent=false
    
    [Install]
    WantedBy=timers.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-cleanup.service nach Wiedereinschalten des Rechners nicht nachgeholt wird.

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

    [Service]
    Type=oneshot
    Nice=15
    IOSchedulingClass=best-effort
    IOSchedulingPriority=7
    EnvironmentFile=-/etc/environment
    EnvironmentFile=%E/backup/backup-env.txt
    #
    ExecStartPre=/bin/bash -lc "restic --no-lock unlock --remove-all --cleanup-cache"
    #
    ExecStart=/usr/bin/systemd-cat -- echo '*** RESTIC FORGET / PRUNE (begin) ***'
    ExecStart=/bin/bash -lc "restic forget --compact --prune --max-unused 10% \
      --tag 'timer' --keep-daily 7 --keep-weekly 4 --keep-monthly 12"
    ExecStart=/usr/bin/systemd-cat -- echo '*** RESTIC FORGET / PRUNE (end) ***'
    #
    ExecStart=/usr/bin/systemd-cat -- echo '*** RESTIC CHECK (begin) ***'
    ExecStart=/bin/bash -lc "restic check"
    ExecStart=/usr/bin/systemd-cat -- echo '*** RESTIC CHECK (end) ***'
    #
    ExecStart=/usr/bin/systemd-cat -- echo '*** CLEAN TRASH (begin) ***'
    ExecStart=/bin/bash -lc "rclone cleanup $REMOTE_REPOSITORY --verbose"
    ExecStart=/usr/bin/systemd-cat -- echo '*** CLEAN TRASH (end) ***'
    #
    ExecStopPost=/bin/bash -lc "restic --no-lock unlock --remove-all --cleanup-cache"
    #
    ExecStopPost=/usr/bin/systemd-cat -- echo '*** EMAIL (begin) ***'
    ExecStopPost=/bin/bash -lc 'backup-cleanup-email.sh "%N"'
    ExecStopPost=/usr/bin/systemd-cat -- echo '*** EMAIL (end) ***'

    Erklärung: backup-cleanup.service beinhaltet die Abläufe, die von backup-cleanup.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. Die Angabe After=backup.service gewährleistet, dass »backup-cleanup« zwar entsprechend des in backup-cleanup.timer eingetragenen Zeitpunkts zum Start vorgesehen wird, aber tatsächlich erst gestartet wird, wenn der Prozess backup.service beendet wurde. Dazu ist die Angabe Type=oneshot in backup.service zwingend. backup-cleanup.service enthält, anders als viele der sonstigen .service-Dateien, keinen [Install]-Abschnitt, womit gewährleistet ist, dass ausschließlich die Angaben in backup-cleanup.timer für den Prozess-Start verantwortlich sind.

  • ~/.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 [email protected]
    
    add_missing_date_header on
    add_missing_from_header on
    
    auth on
    user [email protected]
    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.

  • ~/.local/bin/backup-email.sh

    #!/bin/bash
    
    # $1 = Unit (short)
    
    # Rolf Niepraschk, 2024-12-11
    
    if [ -z ${MAIL_TO} ]; then exit 0; fi
    
    HOST=$(hostname)
    
    SNAPSHOT=$(restic snapshots --host ${HOST} --json latest 2>/dev/null)
    LAST=$(echo ${SNAPSHOT} | jq -r '.[-1].id')
    PREV=$(echo ${SNAPSHOT} | jq -r '.[-1].parent')
    if [ ${PREV} = 'null' ]; 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; format=flowed
    Content-Transfer-Encoding: 8bit
    MIME-Version: 1.0
    
    ${MESSAGE1}
    
    ${MESSAGE2}
    EOF
    
    exit 0

    Das Skript muss ausführbar gemacht werden:

    chmod ugo+x  ~/.local/bin/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.

  • ~/.local/bin/backup-cleanup-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  ~/.local/bin/backup-cleanup-email.sh

    Erklärung: Wird von backup-cleanup.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-cleanup-email.sh backup-cleanup )

    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 Online-Speicher anlegen

( set -a ; source ~/.config/backup/backup-env.txt; rclone mkdir MyGMX: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-cleanup.timer
systemctl --user start  backup-cleanup.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-Lauf, 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 ähnlicher Weise.

Rolf Niepraschk, 12/2024, Lizenz: MIT (siehe Quelltext dieser Datei)

@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