SSD Nodes Learn 🎉 VPS ab $5.50/Monat
Anleitungen Matt ConnorVon Matt Connor

Docker-Compose-Stack sichern und upgraden

Erfahren Sie, welche vier Bestandteile Sie sichern müssen, warum laufende Datenbankdateien nicht genügen und wie Sie Restore und Upgrade zuverlässig prüfen.

Was eine Sicherung eines Docker-Compose-Stacks enthalten muss

Eine Sicherung eines Docker-Compose-Stacks muss vier getrennte Bestandteile enthalten. Fehlt einer davon, kann die Anwendung nicht wiederhergestellt werden: die Compose-Datei, die danebenliegende .env, der Inhalt jedes Volumes und ein mit dem eigenen Client der Datenbank erstellter Datenbank-Dump. Das Kopieren der Datenbankdateien, während ihr Container läuft, ist keine Sicherung. Für Upgrades gilt dieselbe Liste sowie eine zusätzliche Regel: Erstellen Sie die Sicherung, bevor Sie die Images mit pull aktualisieren. Schema-Migrationen sind für die Vorwärtsmigration ausgelegt, und die meisten Projekte bieten keinen Rückweg.

Alle folgenden Schritte setzen voraus, dass der Stack bereits bereitgestellt ist und docker compose ps seinen laufenden Zustand anzeigt. Die Beispiele verwenden ein Projektverzeichnis unter /srv/myapp mit den Diensten app und db. Ersetzen Sie diese durch Ihre eigenen Namen. Die Befehle bleiben bewusst allgemein, weil die wichtigen Bestandteile, die Volumes und die Datenbank, unabhängig von der Anwendung auf dieselbe Weise funktionieren.

Ermitteln Sie, was Ihr Stack tatsächlich speichert

cd /srv/myapp
docker compose ps
docker compose config --volumes
docker volume ls --filter label=com.docker.compose.project=myapp

docker compose config --volumes gibt die Kurznamen der benannten Volumes aus, die Ihre Datei deklariert. docker volume ls gibt die Namen aus, die diese Volumes auf der Festplatte tatsächlich haben. Die beiden Listen unterscheiden sich, weil Compose den Projektnamen voranstellt: Ein in der Datei als db_data eingetragenes Volume existiert als myapp_db_data. Der Projektnamen wird standardmäßig aus dem Verzeichnisnamen abgeleitet. Wenn Sie das Verzeichnis umbenennen, verweist der Stack daher auf einen neuen Satz leerer Volumes. Die alten Volumes bleiben mit Ihren Daten erhalten. Für jeden folgenden Befehl benötigen Sie den tatsächlichen Namen aus docker volume ls.

Bind-Mounts erscheinen in keiner der beiden Listen. In der Compose-Datei sind das die Einträge mit einem Hostpfad links vom Doppelpunkt, ./config:/app/config. Sie sind gewöhnliche Verzeichnisse auf dem Host und können daher mit gewöhnlichen Werkzeugen bearbeitet werden. Benannte Volumes liegen unter /var/lib/docker/volumes/. docker volume inspect --format '{{.Mountpoint}}' myapp_db_data gibt den exakten Pfad eines Volumes aus. Welche Variante Ihr Stack verwendet, bestimmt, wie Sie ihn kopieren. Bind-Mounts und benannte Volumes beschreibt die Vor- und Nachteile ausführlich.

Ordnen Sie die gefundenen Daten nun zwei Gruppen zu. Einige Volumes enthalten Zustände, die nichts anderes wiederherstellen kann: hochgeladene Dateien, erzeugte Schlüssel, die Datenbank selbst und alle Eingaben eines Benutzers in der Anwendung. Andere Volumes enthalten abgeleitete Daten wie Vorschaubilder und Suchindizes, die die Anwendung selbst wiederherstellt. Die Sicherung der zweiten Gruppe benötigt Speicherplatz und verlängert die Wiederherstellung, bringt aber keinen zusätzlichen Nutzen. Ein Redis-Cache-Volume ist das deutlichste Beispiel: Sein Verlust kostet eine langsame erste Anfrage.

Compose-Datei und .env-Datei sichern

Beide Dateien liegen nebeneinander auf dem Host, und keine von ihnen befindet sich in einem Volume. Die .env enthält das Datenbankpasswort, das Anwendungsgeheimnis und alle API-Tokens. Sie ist daher die Datei, die aus einer Ansammlung von Volumes wieder eine funktionierende Anwendung macht. Normalerweise ist sie außerdem in .gitignore aufgeführt. Dadurch schließt der Plan „Meine Konfiguration befindet sich in git“ genau die einzelne Datei aus, die am wichtigsten ist. Secrets in einer env-Datei speichern ist das richtige Vorgehen. Damit entsteht auch eine entsprechende Pflicht für Ihr Backup.

sudo install -d -m 700 -o "$USER" -g "$(id -gn)" /srv/backups/myapp
cp -a compose.yaml .env /srv/backups/myapp/
chmod 600 /srv/backups/myapp/.env

Kopieren Sie jede Compose-Datei, die der Stack verwendet, nicht nur die erste. Ein mit -f compose.yaml -f compose.prod.yaml gestarteter Stack benötigt beide Dateien, um auf dieselbe Weise wiederhergestellt zu werden. Wie mehrere Compose-Dateien zusammengeführt werden bestimmt, welche Werte tatsächlich den Container erreichen.

Eine wichtige Warnung verbindet .env mit den Volumes. Das offizielle Postgres-Image liest POSTGRES_PASSWORD nur, wenn es ein leeres Datenverzeichnis initialisiert. Eine spätere Änderung dieses Werts ändert das Passwort in der Datenbank nicht. Wenn Sie das Volume vom letzten Monat zusammen mit der heutigen .env wiederherstellen, kann die Anwendung keine Verbindung mit FATAL: password authentication failed for user "appuser" herstellen, obwohl bei der Prüfung beide Dateien korrekt aussehen. Bewahren Sie .env und die Volumes aus demselben Zeitpunkt gemeinsam in demselben Backup auf.

Die Datenbank mit ihrem eigenen Client sichern

Ein Datenbankserver schreibt fortlaufend in seine Dateien. Ein tar von /var/lib/postgresql/data, das während des laufenden Servers erstellt wird, kopiert einige Seiten vor einem Schreibvorgang und andere danach. Das Archiv enthält dadurch eine Mischung verschiedener Zeitpunkte, die sich möglicherweise nicht wiederherstellen lässt. Ein Dump-Tool liest innerhalb einer einzelnen Transaktion. Die Datei enthält dadurch einen konsistenten Zeitpunkt. Dieser Unterschied trennt ein Backup von einer Kopie.

docker compose exec -T db sh -c \
  'pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Fc' \
  > /srv/backups/myapp/db-$(date +%F).dump

Behalten Sie das -T bei. Es deaktiviert die TTY-Zuweisung. Wenn eine TTY verbunden ist, verändert Docker den Ausgabestream auf dem Weg zu Ihrer Shell. Dadurch wird ein binärer Dump beschädigt. Sie bemerken das erst, wenn die Wiederherstellung fehlschlägt. Auch die einfachen Anführungszeichen sind wichtig. Sie verhindern, dass Ihre Host-Shell $POSTGRES_USER expandiert. Stattdessen expandiert die Shell im Container die Variable und verwendet dabei die Werte, die die Compose-Datei dort bereits setzt. -Fc schreibt das benutzerdefinierte Format. Dieses Format wird während des Schreibens komprimiert. Mit pg_restore können Sie später einzelne Objekte daraus auswählen.

Rollen und ihre Passwörter befinden sich außerhalb einer einzelnen Datenbank. Sichern Sie daher auch diese:

docker compose exec -T db sh -c 'pg_dumpall -U "$POSTGRES_USER" --globals-only' \
  > /srv/backups/myapp/globals.sql

Prüfen Sie anschließend, ob die Datei tatsächlich ein Dump und keine Fehlermeldung ist:

ls -lh /srv/backups/myapp/
head -c 5 /srv/backups/myapp/db-$(date +%F).dump

Ein Dump im benutzerdefinierten Format beginnt mit den fünf Bytes PGDMP. Eine Datei mit 0 Bytes oder eine Datei, die mit pg_dump: beginnt, bedeutet, dass der Befehl fehlgeschlagen ist. Die Shell erstellt die Ausgabedatei, bevor der Befehl ausgeführt wird. Daher bleibt auch nach einem fehlgeschlagenen Dump eine Datei mit einem plausiblen Namen und einem plausiblen Zeitstempel zurück. Das ist der häufigste unbemerkte Fehler bei Backups.

Bei MariaDB oder MySQL ändert sich der Client, nicht aber das Vorgehen:

docker compose exec -T db sh -c \
  'mariadb-dump -u root -p"$MARIADB_ROOT_PASSWORD" --single-transaction --databases "$MARIADB_DATABASE"' \
  > /srv/backups/myapp/db-$(date +%F).sql

--single-transaction erstellt einen konsistenten Dump von InnoDB-Tabellen, ohne Schreibvorgänge zu blockieren. Beim MySQL-Image lautet der Befehl mysqldump. Die Variablen sind MYSQL_ROOT_PASSWORD und MYSQL_DATABASE. Bei aktuellen MariaDB-Images funktioniert mysqldump weiterhin als Kompatibilitätsname für mariadb-dump. Beachten Sie, dass ein in der Befehlszeile angegebenes Passwort während der gesamten Laufzeit des Dumps in der Prozessliste des Containers sichtbar ist.

SQLite erfordert eine eigene Vorgehensweise. Die Datenbank besteht aus einer Datei. Aktuelle Transaktionen können jedoch weiterhin in einer separaten -wal-Datei daneben liegen. Wenn Sie nur .db kopieren, fehlen der Datenbank die neuesten Schreibvorgänge. Wenn das Image den Client enthält, schreibt sqlite3 /data/app.db ".backup '/data/app-backup.db'" eine konsistente Kopie, während die Anwendung läuft. Wenn der Client nicht enthalten ist, stoppen Sie den Container. Kopieren Sie dann die Datei .db zusammen mit den zugehörigen Dateien -wal und -shm.

Wenn Ihre Datenbank auf dem Host und nicht innerhalb des Stacks läuft, gelten dieselben Befehle ohne das Präfix docker compose exec. Der Artikel Datenbank in Docker oder auf dem Host betreiben ist vor dem nächsten Neuaufbau lesenswert.

Volumes erfassen

Ein benanntes Volume hat keinen Host-Pfad, den Sie manuell bearbeiten sollten. Binden Sie es daher in einen Wegwerfcontainer ein und archivieren Sie es von dort.

docker run --rm \
  -v myapp_uploads:/data:ro \
  -v /srv/backups/myapp:/backup \
  alpine:3 tar czf /backup/uploads.tar.gz -C /data .

Der Hilfscontainer bindet das Volume unter /data schreibgeschützt und Ihr Backup-Verzeichnis unter /backup ein. Anschließend schreibt er das Archiv auf die Host-Seite. --rm entfernt den Hilfscontainer, sobald tar beendet wird. :ro ist wichtig, weil ein falsch eingegebener tar-Befehl dadurch die Quelle nicht beschädigen kann. -C /data . sorgt dafür, dass die Wiederherstellung am richtigen Ort landet: Der Befehl speichert jeden Pfad relativ zum Volume-Root. Verwenden Sie stattdessen tar czf /backup/uploads.tar.gz /data, erhält jeder Pfad ein führendes data/. Dadurch erstellt die Wiederherstellung /data/data innerhalb des Volumes, und die Anwendung sieht ein leeres Verzeichnis. Das Archiv gehört root, weil tar im Container als root ausgeführt wurde. Führen Sie sudo chown "$USER" /srv/backups/myapp/uploads.tar.gz aus, wenn das Probleme verursacht. Lesen Sie außerdem wie PUID und PGID den Dateibesitz bestimmen, wenn die wiederhergestellten Dateien für die Anwendung nicht lesbar sind.

Führen Sie den Befehl einmal pro benanntem Volume aus. Bind-Mounts benötigen keinen Container: tar czf /srv/backups/myapp/config.tar.gz -C /srv/myapp/config . erledigt dieselbe Aufgabe auf dem Host.

Entscheiden Sie für jedes Volume, ob die Anwendung angehalten werden muss. Ein laufendes tar-Backup eines Volumes, in das die Anwendung Dateien direkt schreibt, kann eine Datei während eines Schreibvorgangs erfassen. Bei einem Upload-Verzeichnis, in das Dateien einmal geschrieben und danach nur gelesen werden, ist dieses Risiko gering. Stoppen Sie den betreffenden Dienst in allen anderen Fällen für die Dauer des Kopiervorgangs mit docker compose stop app und führen Sie danach docker compose start app aus. stop lässt die Container und Volumes bestehen. Genau das ist hier gewünscht. Lesen Sie den Unterschied zwischen down und stop, bevor Sie einen der beiden Befehle eingeben.

Betrachten Sie ein tar-Archiv des Datenbank-Volumes nicht als Datenbank-Backup. Der Dump ist das Backup. Ein Volume-Archiv einer gestoppten Datenbank ist ein nützlicher schneller Wiederherstellungspfad, mehr nicht.

Die Reihenfolge der Arbeitsschritte

  1. Kopieren Sie die Compose-Dateien und die .env in das Backup-Verzeichnis.
  2. Erstellen Sie einen Datenbank-Dump, solange die Datenbank noch läuft.
  3. Stoppen Sie den App-Container, wenn sich seine Volumes direkt ändern.
  4. Archivieren Sie jedes benannte Volume und jedes Verzeichnis eines Bind-Mounts.
  5. Starten Sie alles, was Sie gestoppt haben, und prüfen Sie den Zustand anschließend mit docker compose ps.
  6. Notieren Sie die Image-Tags und Digests, mit denen der Stack läuft.
  7. Kopieren Sie das gesamte Backup-Verzeichnis von diesem Server auf ein anderes System.

Schritt 7 wird häufig auf später verschoben.

Kopien vom Server auslagern

Ein Backup auf derselben Festplatte wie der Stack schützt Sie vor eigenen Fehlern, aber vor nichts anderem. Ein ausgefallenes Volume, ein gelöschter Server oder ein verlorenes Konto kann beide Kopien gleichzeitig unbrauchbar machen. Übertragen Sie das Verzeichnis nach einem festen Zeitplan mit einer Aufbewahrungsrichtlinie auf einen Speicherort, der nicht auf diesem VPS liegt. restic-Backups von einem VPS behandelt die Einrichtung des Repositorys, die Aufbewahrungsoptionen und den Prüf-Befehl. Diese Punkte müssen hier daher nicht wiederholt werden.

restic kann den Dump auch direkt aus einer Pipe lesen. Dadurch bleibt die Datenbank im Klartext vollständig von der Festplatte fern:

docker compose exec -T db sh -c 'pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Fc' \
  | restic backup --stdin --stdin-filename db.dump

Unabhängig vom verwendeten Tool sollten Sie den Zeitplan in einem systemd-Timer oder einem cron-Job hinterlegen. Der Job muss Fehler an eine Stelle melden, an der Sie sie sehen. Ein Backup-Skript, dessen Ausgabe nirgendwo ankommt, kann sechs Monate lang nicht funktionieren, ohne dass es jemand bemerkt.

Nachweis, dass das Backup mit einem Wiederherstellungstest funktioniert

Ein Backup, aus dem noch niemand eine Wiederherstellung durchgeführt hat, ist nur eine Annahme. Der folgende Test stellt das Backup in einem zweiten Stack wieder her, der neben dem ersten läuft. Die Produktion bleibt dabei erreichbar, und keine Eingabe kann sie erreichen.

Entscheidend ist der Projektname. Compose übernimmt ihn aus dem Verzeichnisnamen und versieht jeden erstellten Container und jedes erstellte Volume damit. Kopieren Sie das Backup in ein neues Verzeichnis. Der wiederhergestellte Stack erhält dadurch automatisch eigene Volumes.

sudo install -d -m 700 -o "$USER" -g "$(id -gn)" /srv/myapp-restore
cd /srv/myapp-restore
cp /srv/backups/myapp/compose.yaml /srv/backups/myapp/.env .

Bearbeiten Sie die kopierte Compose-Datei so, dass der veröffentlichte Host-Port nicht mit dem laufenden Stack kollidiert: Ersetzen Sie 8080:8080 durch 18080:8080, oder ändern Sie die Variable, die den Port in der kopierten .env festlegt. Erstellen Sie anschließend die Container und ihre leeren Volumes, ohne etwas zu starten:

docker compose create
docker volume ls --filter label=com.docker.compose.project=myapp-restore

Der zweite Befehl sollte dieselben Volumenamen wie die Produktion auflisten, mit myapp-restore_ als Präfix. Befüllen Sie die Volumes, starten Sie nur die Datenbank und laden Sie den Dump:

docker run --rm -v myapp-restore_uploads:/data -v /srv/backups/myapp:/backup \
  alpine:3 tar xzf /backup/uploads.tar.gz -C /data
docker compose up -d db
docker compose exec -T db sh -c \
  'pg_restore -U "$POSTGRES_USER" -d "$POSTGRES_DB" --clean --if-exists' \
  < /srv/backups/myapp/db-2026-08-16.dump

--clean --if-exists löscht jedes Objekt vor der erneuten Erstellung. Dadurch kann die Wiederherstellung wiederholt werden. Ohne diese Option bricht ein zweiter Lauf in einer Datenbank, die diese Tabellen bereits enthält, mit pg_restore: error: could not execute query: ERROR: relation "users" already exists ab.

Starten Sie anschließend die übrigen Dienste und prüfen Sie den Stack so, wie es ein Benutzer tun würde:

docker compose up -d --wait
docker compose exec -T db sh -c 'psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "\dt"'
docker compose logs --tail=50

docker compose up -d --wait wartet, bis jeder Dienst den Status running oder healthy meldet. Wenn ein Dienst diesen Zustand nie erreicht, wird ein Exit-Code ungleich 0 zurückgegeben. Dadurch lässt sich dieser Schritt skripten. Wenn ein Dienst nie healthy wird, zeigt docker compose ps seinen Status an. Compose-Healthchecks erläutert, welche Information diese Spalte enthält. Öffnen Sie die Anwendung anschließend über den alternativen Port und melden Sie sich mit einem echten Konto an. Schreiben Sie einen Datensatz und öffnen Sie eine Datei, die in einem Volume liegt. Dieses Paar liefert den Nachweis: Der Dump wurde wiederhergestellt, das Volume wurde wiederhergestellt, und beide stimmen überein. Ein Test, der nur das Anzeigen der Anmeldeseite bestätigt, sagt nichts über Ihre Daten aus.

Löschen Sie den Test-Stack, sobald der Test erfolgreich war:

docker compose down -v

Nur an dieser Stelle ist -v das richtige Flag. Im Produktionsverzeichnis löscht derselbe Befehl die Volumes, die Sie schützen möchten.

So aktualisieren Sie einen Compose-Stack

Lesen Sie die Release Notes für jede Version zwischen der aktuell eingesetzten und der gewünschten Version. Suchen Sie darin nach den Begriffen breaking und migration. Projekte, die keine Sprünge über mehrere Hauptversionen unterstützen, weisen dort darauf hin. Eine Migration, die nicht ausgeführt werden kann, meldet dies möglicherweise erst, nachdem sie bereits einen Teil des Schemas geändert hat.

Halten Sie vor jeder Änderung fest, welche Version aktuell läuft:

docker compose images
docker image inspect --format '{{index .RepoDigests 0}}' postgres:16.4

docker compose images listet das Image und den Tag auf, die jeder Dienst derzeit verwendet. Nur der Digest bezeichnet ein Image eindeutig, weil ein Tag jederzeit auf ein anderes Image zeigen kann.

Nehmen Sie das Backup aus den vorherigen Abschnitten und kopieren Sie es vom Server herunter. Tun Sie dies auch bei einem Patch-Release. Upgrades, die problemlos verlaufen, sind genau die, auf deren Vorbereitung viele verzichten.

Fixieren Sie anschließend die Version in der Compose-Datei, weil latest keine Version ist:

services:
  db:
    image: postgres:16.4

Mit image: postgres:latest ruft docker compose pull das Image ab, auf das der Tag heute zeigt. Sie können dann nicht eindeutig angeben, welches Image gestern ausgeführt wurde. Ein fixierter Tag macht aus dem Upgrade eine einzeilige Änderung, die Sie in git diff nachvollziehen und mit einer weiteren Änderung zurücknehmen können. Fixieren Sie das Anwendungs-Image auf dieselbe Weise. Verwenden Sie dafür die exakte Version von der Release-Seite des Projekts.

Laden Sie das Image herunter und erstellen Sie die Container neu:

docker compose pull
docker compose up -d --wait

docker compose up -d vergleicht die Datei mit den laufenden Containern und erstellt nur die Dienste neu, deren Image oder Konfiguration geändert wurde. Benannte Volumes werden nicht verändert. Der neue Container startet daher mit den vorhandenen Daten. Das ist der Zweck dieses Vorgangs und zugleich das Risiko, weil die Schema-Migration der neuen Version normalerweise beim ersten Start ausgeführt wird.

Beobachten Sie den Vorgang:

docker compose ps
docker compose logs -f --tail=100 app

Bei einem fehlgeschlagenen Container steht Exited (1) in der Spalte STATUS von docker compose ps. Die Ursache finden Sie in den letzten Zeilen des Logs. Migrationsfehler werden dort deutlich angezeigt und sind an anderer Stelle nicht sichtbar. Wenn sich die Logs beruhigt haben, melden Sie sich an und verwenden Sie die Anwendung eine Minute lang.

Wenn docker compose pull mit no space left on device beendet wird, sind alte Image-Layer meist die Ursache. Durch ungenutzte Docker-Images entfernen wird der Speicherplatz wieder freigegeben. Führen Sie das Entfernen erst durch, nachdem sich das Upgrade bewährt hat, nicht vorher. Für ein schnelles Rollback werden diese alten Layer benötigt.

So führen Sie ein Rollback durch, wenn das Upgrade fehlschlägt

Es gibt zwei Fälle, die sich beim Aufwand deutlich unterscheiden. Wenn die neue Version das Schema nicht geändert hat, besteht das Rollback aus einer einzigen Zeile: Tragen Sie den alten Tag wieder in der Compose-Datei ein und führen Sie docker compose up -d aus. Der Container wird ersetzt, die Volumes bleiben erhalten, und der alte Code kann die von ihm geschriebenen Daten lesen.

Wenn die neue Version das Schema migriert hat, kann der alte Code die Daten nicht mehr lesen. Migrationen sind für die Vorwärtsmigration ausgelegt, und die meisten Projekte liefern überhaupt kein Downgrade-Skript aus. Daher startet die alte Version und schlägt anschließend bei der ersten Abfrage einer umbenannten oder entfernten Spalte mit Fehlern der Form ERROR: column "avatar_url" does not exist fehl. Der Rückweg führt über den Dump, den Sie vor dem Pull erstellt haben: Tragen Sie den alten Tag wieder ein, entfernen Sie das Datenbank-Volume, erstellen Sie es leer neu, spielen Sie den Dump ein und starten Sie den Dienst. Ohne diesen Dump gibt es keinen Rückweg. Genau deshalb wird das Backup vor dem Pull erstellt.

Bei den Major-Versionen von Postgres ist dieses Problem besonders ausgeprägt. Es überrascht viele, weil der Fehler beim Upgrade und nicht beim Rollback auftritt. Das Format auf der Festplatte ändert sich mit jeder Major-Version. Ändern Sie postgres:16.4 in postgres:17.2, führen Sie docker compose up -d aus, und der neue Server verweigert den Start:

FATAL:  database files are incompatible with server
DETAIL:  The data directory was initialized by PostgreSQL version 16, which is not compatible with this version 17.2.

Das Image führt pg_upgrade nicht automatisch für Sie aus. Der unterstützte Weg innerhalb eines Compose-Stacks lautet: Dump erstellen, ersetzen, wiederherstellen. Erstellen Sie den Dump, während die alte Version noch läuft, führen Sie docker compose down aus, entfernen Sie das Datenbank-Volume, setzen Sie den neuen Tag, führen Sie docker compose create für ein neues leeres Datenverzeichnis aus, starten Sie die Datenbank, spielen Sie den Dump ein und starten Sie anschließend die übrigen Dienste. Bewahren Sie den alten Dump auf, bis die neue Major-Version einen Tag lang echten Netzwerkverkehr verarbeitet hat. Minor-Upgrades innerhalb einer Major-Version, beispielsweise von 16.4 auf 16.9, benötigen diese Schritte nicht, weil das Format zwischen diesen Versionen stabil bleibt und der Container einfach startet.

Sind VPS-Snapshots ein Backup?

Sie sind eine Ergänzung zu einem Backup. Beide Verfahren versagen auf unterschiedliche Weise. Ein Snapshot kopiert die gesamte Festplatte auf dem Hypervisor. Dadurch lässt sich die gesamte Maschine innerhalb weniger Minuten wiederherstellen, einschließlich der Daten, deren Sicherung Sie vergessen haben. Dafür ist ein Snapshot das richtige Werkzeug: Ein Upgrade hat den Server beschädigt, und Sie möchten den Zustand von vor zwanzig Minuten wiederherstellen.

Für alle anderen Aufgaben ist ein Snapshot nur eingeschränkt geeignet. Die Granularität umfasst die gesamte Maschine. Um eine gelöschte Tabelle wiederherzustellen, müssen Sie daher einen vollständigen Server an einem anderen Ort wiederherstellen und die Tabelle daraus extrahieren. Die Aufbewahrungsdauer ist normalerweise kurz. Die Kopien liegen üblicherweise im selben Provider-Konto wie der Server. Wenn Sie den Zugriff auf das Konto verlieren, sind Server und Snapshots gleichzeitig nicht mehr verfügbar. Ein Snapshot einer laufenden Maschine kann die Datenbank während eines Schreibvorgangs erfassen. Beim ersten Start führt die Datenbank dann eine Crash-Recovery durch. Eine zu diesem Zeitpunkt noch laufende Transaktion geht verloren.

Verwenden Sie beide Verfahren. Der Snapshot ist die Rückgängig-Funktion für ein Upgrade-Fenster. Das Dump ist die Kopie, die den Verlust eines Kontos übersteht. wie sich Snapshots und Backups unterscheiden zeigt, welche Ausfälle die beiden Verfahren tatsächlich abdecken. Dasselbe Backup-Verzeichnis macht auch das Verschieben eines Stacks auf einen neuen VPS zu einer Routineaufgabe statt zu einem Neuaufbau aus dem Gedächtnis.

Was schiefgeht und was Sie sehen

Das Volume-Flag bei down. docker compose down -v entfernt die benannten Volumes, die in der Datei deklariert sind. Compose bestätigt dies mit einer Zeile, die Volume myapp_db_data Removed enthält. Das lässt sich nicht rückgängig machen. Das einfache docker compose down lässt die Volumes unverändert. Verwenden Sie die Langform docker compose down --volumes. Dadurch müssen Sie das destruktive Flag vollständig ausschreiben.

Ein Dump ohne die erwartete Zeichenfolge. pg_restore: error: did not find magic string in file header bedeutet, dass die Datei kein Archiv ist. Die häufigste Ursache ist ein fehlendes -T bei docker compose exec. Wenn ein TTY verbunden ist, wird der Datenstrom auf dem Weg zur Shell umgesetzt. Dadurch wird der binäre Dump beschädigt. Erstellen Sie den Dump erneut mit -T. Prüfen Sie anschließend die ersten fünf Bytes mit head -c 5.

Ein Passwort, das sich nicht ändern lässt. FATAL: password authentication failed for user "appuser" nach einer Wiederherstellung bedeutet, dass .env und das Datenverzeichnis aus unterschiedlichen Zeitpunkten stammen. Das Image setzt dieses Passwort nur beim Erstellen eines leeren Datenverzeichnisses. Eine spätere Änderung von .env ändert daher nichts innerhalb der Datenbank. Stellen Sie das passende .env wieder her. Alternativ ändern Sie das Passwort innerhalb der Datenbank mit ALTER USER.

Ein zweites, leeres Volume. Docker erstellt ein Volume bei Bedarf. Daher schreibt docker run -v myapp_upload:/data mit dem fehlenden s in ein brandneues, leeres Volume und meldet Erfolg. docker volume ls zeigt anschließend beide Namen. Eines der Volumes enthält dabei keine Daten. Übernehmen Sie die Volume-Namen aus docker volume ls, statt sie aus dem Gedächtnis einzugeben.

Eine Wiederherstellung im Produktivsystem. Wenn Sie die Wiederherstellungsbefehle in /srv/myapp statt in /srv/myapp-restore ausführen, überschreiben Sie die aktiven Daten mit dem Backup. Die Befehle sehen in beiden Verzeichnissen identisch aus. Prüfen Sie pwd vor jedem Wiederherstellungsbefehl. Führen Sie den Testlauf in einem eigenen Verzeichnis aus.

FAQ

Löscht docker compose down meine Daten?

Nein. docker compose down entfernt die Container und das Standardnetzwerk, lässt benannte Volumes und Bind-Mounts jedoch unverändert. docker compose down -v entfernt die benannten Volumes, die Ihre Datei definiert, und das ist dauerhaft. Bind-Mounts sind Verzeichnisse auf dem Host, daher entfernt Compose sie nie. Wenn Sie die Dienste während einer Sicherung anhalten und alles andere unverändert lassen möchten, verwenden Sie stattdessen docker compose stop.

Kann ich das Postgres-Datenverzeichnis kopieren, anstatt pg_dump auszuführen?

Nur wenn der Container angehalten ist. Während der Server läuft, ändern sich seine Dateien, und die Kopie kann einen nicht konsistenten Zwischenstand enthalten, der sich nicht wiederherstellen lässt. Eine Kopie auf Dateiebene ist außerdem an eine bestimmte Postgres-Hauptversion gebunden und startet daher nicht unter einer anderen Version. Stoppen Sie den Container, archivieren Sie das Volume, starten Sie den Container wieder und betrachten Sie das Ergebnis als schnellen Weg zum Wiederaufbau, nicht als einzige Sicherung. Der Dump ist die portable Kopie und die Kopie, aus der Sie wiederherstellen.

Wie aktualisiere ich Postgres in Compose auf eine neue Hauptversion?

Das Ändern des Tags reicht nicht aus. Der neue Server verweigert den Start mit dem alten Datenverzeichnis und protokolliert The data directory was initialized by PostgreSQL version 16, which is not compatible with this version 17.2. Führen Sie pg_dump aus, solange die alte Version noch läuft, anschließend docker compose down, entfernen Sie das Datenbank-Volume, setzen Sie den neuen Tag, führen Sie docker compose create für ein neues leeres Volume aus, starten Sie die Datenbank und stellen Sie den Dump darin wieder her. Bewahren Sie den alten Dump auf, bis die neue Version echten Netzwerkverkehr verarbeitet hat.

Wie oft sollten Sicherungen ausgeführt werden, und wie lange sollte ich sie aufbewahren?

Richten Sie das Intervall danach aus, wie viel Arbeit Sie im Fehlerfall erneut durchführen können. Für einen persönlichen Stack oder einen Stack eines kleinen Teams eignet sich eine tägliche Sicherung sowie eine zusätzliche manuelle Sicherung unmittelbar vor jedem Upgrade. Bewahren Sie für die Aufbewahrung genügend Historie auf, um Schäden abzudecken, die Sie nicht sofort bemerkt haben. Eine am Freitag entdeckte beschädigte Tabelle lässt sich mit der Kopie vom Donnerstagabend nicht beheben. restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune ist eine sinnvolle Ausgangsrichtlinie. Unabhängig vom Zeitplan sollten Sie einmal pro Quartal eine Wiederherstellung durchführen. Bis Sie das getan haben, haben Sie keine Sicherungen, sondern Dateien.

Muss ich den gesamten Stack anhalten, um eine Sicherung zu erstellen?

In der Regel nicht. Der Datenbank-Dump ist konsistent, während der Server läuft, daher benötigt die Datenbank keine Ausfallzeit. Die Volumes sind der entscheidende Punkt. Wenn die Anwendung nur Dateien hinzufügt, beispielsweise in einem Upload-Verzeichnis, ist ein laufendes Archiv in der Regel sicher genug. Wenn sie Dateien direkt überschreibt, halten Sie diesen einen Dienst für die Dauer der Kopie mit docker compose stop app an und starten Sie ihn anschließend wieder. Die Anwendung anzuhalten, während die Datenbank weiterläuft, ist normalerweise das kürzeste sichere Zeitfenster, das Sie einrichten können.