Kinetiqa Blog Homepage
Eine Person steckt eine SD-Karte in ein Kartenlesegerät. Sie sitzt am Schreibtisch, im Hintergrund sind ein Bildschirm und eine digitale Spiegelreflex-Kamera zu sehen.

Effiziente Backups für kleine WordPress-Seiten

Im Agenturalltag kommt man oft in die Situation, eine kleine WordPress-Seite eines Kunden aktualisieren zu müssen. Dann steht stets ein kleines Banner auf der Admin-Seite:

Meldung im WordPress-Backend: „Wichtig: Vor der Aktualisierung bitte deine Datenbank und Dateien sichern.“

Schon setzt das schlechte Gewissen ein: „Habe ich funktionierende Backups?“, „Kann ich den Stand vor der Aktualisierung wieder herstellen?“.

Backup-Methoden

Man klickt den Knopf deutlich beruhigter, wenn man sich seiner Backupstrategie sicher sein kann. Es gibt mehrere Möglichkeiten, von denen gelegentlich sogar die „ich verlasse mich auf den Provider“-Methode ausreichend sein mag.

Will man die Kontrolle selbst behalten, muss man diverse Punkte gegeneinander abwiegen. Bei lange laufenden und intensiv genutzten Blogs kann sich die Größe des Docroots schon mal im Dutzende Gigabyte-Bereich befinden. Dann wird man andere Lösungen brauchen als die, die wir hier vorstellen, z.B. indem man Features eines Snapshot-fähigen Dateisystems nutzt.

Ist die Datenbank hochfrequentiert, muss man auch hier beim Dump spezielle Vorkehrungen treffen, indem man z.B. nicht SQL-Dateien exportiert, sondern mit Tools wie mariadb-backup die tatsächlichen DB-Dateien kopiert.

Aber es gibt eine große Zahl an kleineren und mittleren WordPress-Blogs, für die das alles übertrieben wäre, bei denen man aber vielleicht doch ein wenig mehr machen möchte, als der Provider anbietet. Für diese Situationen ist die folgende Methode geeignet.

Git-basiertes Backup

Wir verwenden das Versionsverwaltungs-Werkzeug Git, um ein Abbild des aktuellen Docroots zu speichern. Das erlaubt, zu einem beliebigen Zeitpunkt in der Vergangenheit zurückzuspringen.

Es wäre allerdings eine schlechte Idee, das Git-Repository als .git-Ordner direkt im Docroot zu haben. Das würde im schlimmsten Fall dem geneigten Angreifer Zugriff auf sensible Daten wie die Datenbank-Zugriffswerte in der wp-config.php geben.

Deshalb machen wir einen kleinen Umweg mit Umgebungsvariablen. Git ist mit einer Vielzahl davon konfigurierbar. Diese Einstellungen können auch in einer zentralen $HOME/.gitconfig oder beim Aufruf über Kommandozeilen-Parameter mitgegeben werden.

Der Vorteil von Umgebungsvariablen ist, dass sie nicht immer verwendet werden (wie bei Werten in Config-Dateien), gleichzeitig aber auch nicht bei jedem Aufruf mitgegeben werden müssen (wie bei Kommandozeilen-Parametern).

Wir benötigen für unsere Strategie sechs Parameter:

GIT_DIR
Diese Variable zeigt auf das Git-Repository (den name.git-Ordner)
GIT_WORK_TREE
Hiermit kann man das Arbeitsverzeichnis der ausgecheckten Dateien bestimmen
GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, GIT_COMMITTER_NAME und GIT_COMMITTER_EMAIL
Die Variablen bestimmen den Namen und die Mailadresse des Änderungs- und Commit-Autors

Zuerst legen wir für unsere Backup-Strategie ein leeres Git-Repository an. Wir lassen es von Git „bare“ konfigurieren, d.h., wir sagen Git, dass es eigentlich kein Arbeitsverzeichnis gibt.

export GIT_DIR="/backups/webseite.git"
test -d "$GIT_DIR" || git init --bare

Wir müssen git init keinen Ordnernamen mitgeben. Es holt sich die Information aus der $GIT_DIR-Variable.

Diesen Schritt müssen wir nur ein Mal machen, er lässt sich aber für 0-Installation-Scripte auch zu Beginn des Backup-Scripts ausführen.

Anschließend können wir das Backup laufen lassen, indem wir das Webseiten-Docroot als Arbeitsverzeichnis und unser neu angelegtes Repository als Git-Ordner zuweisen. Den Namen des Committers wählen wir so, dass klar ist, wer hier gerade Änderungen einpflegt. Wir empfehlen, eine tatsächliche E-Mail-Adresse zu verwenden. So kann man im Bedarfsfall schnell die richtige Person anschreiben.

export GIT_DIR="/backups/webseite.git"
export GIT_WORK_TREE="/var/www/html"
export GIT_AUTHOR_NAME="Backup-Script"
export GIT_AUTHOR_EMAIL="support@example.com"
export GIT_COMMITTER_NAME="Backup-Script"
export GIT_COMMITTER_EMAIL="support@example.com"

# wechsle ins Docroot, sonst klappt "git add" nicht.
cd "$GIT_WORK_TREE"

# füge alle Änderungen dem Repository hinzu
git add --all .

# committe die Änderungen falls notwendig
git diff --staged --quiet || git commit --message="automatic backup"

Fertig. Der aktuelle Stand des Docroots ist jetzt für alle Zeiten im Git-Repository verewigt. Git legt außerdem einen Zeitstempel für den Commit an, sodass wir stets nachvollziehen können, von wann das Backup ist.

Was macht die Zeile mit dem git diff eigentlich hier? Das ist ein kleiner Umweg, der verhindert, dass wir leere Commits anlegen (bzw. dass Git sich über potentiell leere Commits beschwert). Mit git diff --staged schauen wir, ob sich durch git add überhaupt etwas geändert hat. Dank --quiet gibt das Kommando nichts aus, sondern kommuniziert sein Ergebnis durch den Rückgabewert: 0 (Erfolg), falls es keine Änderungen gab, 1 (Fehler) bei Änderungen.

Was ist mit der Datenbank?

Hier gibt es mehrere Möglichkeiten: Man kann sie ebenfalls versionsverwalten, indem man einen SQL-Dump anlegt und an geeigneter Stelle Git in den Index unterjubelt. (Eine Möglichkeit wäre, das Docroot erst in einen temporären Ordner zu kopieren und dann dort die SQL-Datei einfach mit abzulegen, bevor man den Commit mit diesem Ordner macht.)

Es ist auch möglich, die Datenbankbackups separat zu verwalten. Das kann man mit wenigen Zeilen Code im Backupscript mit einbauen:

#!/bin/bash

set -euo pipefail

# Backup-Ordner anlegen, falls er nicht existiert
mkdir -p /backups/db

# Backup anlegen und auch gleich gzippen
export MYSQL_PWD="$DATENBANK_PASSWORT"
mysqldump \
    --host="$DATENBANK_HOST" \
    --user="$DATENBANK_NUTZER" \
    --no-create-db \
    --default-character-set=utf8mb4 \
    "$DATENBANK_NAME" \
| \
gzip -9 >"/backups/db/$(date +'%Y-%m-%d-%H%M%S').sql.gz"

# Backups löschen, die älter als 31 Tage sind
find /backups/db -type f -name "*.gz" -mtime +31 -exec rm -f "{}" \;

Hinweis: Nutzer von WP-CLI haben es noch einfacher, an einen Dump zu kommen, denn da ist ein entsprechendes Kommando bereits eingebaut. Wir empfehlen Ihnen, sich dieses WordPress-Werkzeug definitiv näher anzusehen, wenn Sie es noch nicht kennen sollten.

Vorteile und Beschränkungen dieser Variante

Es ist wichtig, sich daran zu erinnern, dass Git-Commits nicht umsonst sind. Jede verbuchte Änderung kostet Plattenplatz. Werden im Blog viele Bilder hochgeladen (und von WordPress dann auch gleich hilfreich in ein halbes Dutzend Thumbnails konvertiert), kann die Größe des Git-Repositories schnell wachsen.

Darüber hinaus ist das Backup nicht atomar. Das soll heißen, dass es durchaus Race Conditions geben kann, wo das Backup mitten während eines Updates läuft und damit halb alte, halb neue Dateien aufzeichnet. Der entsprechende Backupstand wäre dann unbenutzbar. Der Vorteil eines Git-Repositorys ist allerdings, dass man in diesem Fall einfach einen Commit weiter in die Vergangenheit springen kann, um die meisten Teile wieder herzustellen.

Ein wesentlicher, in Git eingebauter Vorteil ist die einfache Möglichkeit, das Backup vom Server weg auf eine andere, sichere Instanz zu pushen. Wenn man dort ein Repository remote verfügbar macht, reicht eine Zeile im Backup-Script für Offsite-Backups:

git push origin

Schon ist die Seite vor einer Vielzahl Kalamitäten gefeit, vom Servercrash bis zu Verschlüsselungstrojanern.

Foto von Julio Lopez auf Unsplash

Nach oben Zur Startseite