borgmatic im Docker-Container: automatisierte Server-Backups mit BorgBackup
borgmatic steuert BorgBackup über eine YAML-Datei. Die Anleitung zeigt den Betrieb im Docker-Container mit festem Tag 2.2.0: Konfiguration, Datenbank-Hooks, erstes Backup, Prüfung, Wiederherstellung mit Prüfsummenvergleich und Monitoring.
Geprüft am 31.10.2026 · für borgmatic (docker-borgmatic) 2.2.0
WerbelinksMit * markierte Links sind Werbelinks: Bei einem Kauf erhalten wir eine Provision, der Preis bleibt gleich. Als Amazon-Partner verdiene ich an qualifizierten Verkäufen. Mehr dazu

borgmatic steuert BorgBackup über eine einzige YAML-Datei: Quellen, Ziele, Aufbewahrung, Datenbank-Dumps, Konsistenzprüfungen und Benachrichtigungen. Es ist kein eigenes Backup-Format, sondern eine Steuerungsschicht über Borg. Diese Anleitung zeigt den Betrieb im offiziellen Container-Image von der Verzeichnisstruktur über das erste Archiv bis zur geprüften Wiederherstellung. Grundlage sind Tests mit borgmatic 2.1.7 (September 2026) und zuletzt am 31.10.2026 mit borgmatic 2.2.0 und Borg 1.4.5 auf einem Linux-Host mit Docker 29.8.2.
Voraussetzungen
- Server: Linux-Host mit Docker Engine und Docker Compose v2, x86-64 oder ARM64 (laut README Images für
linux/amd64undlinux/arm64, also auch Raspberry Pi mit 64-Bit-System). - CPU und RAM: 1 Kern und 512 MB RAM für kleine Datenmengen, 2 Kerne für Kompression. Der RAM-Bedarf wächst mit der Zahl der Dateien und Chunks, weil Borg den Index im Speicher hält. AVX ist nicht nötig.
- Speicher: Das Repository wächst mit dem Änderungsanteil, nicht mit der Zahl der Sicherungen. Legen Sie es auf ein lokales Linux-Dateisystem (ext4, XFS, Btrfs), nicht auf eine SMB-Freigabe.
- Offsite-Ziel (empfohlen): ein zweiter Server mit SSH-Zugang und installiertem Borg.
| Eckdaten | Wert |
|---|---|
| Image | ghcr.io/borgmatic-collective/borgmatic:2.2.0 (borgmatic 2.2.0, Borg 1.4.5, Alpine) |
| Ports | keine |
| Konfiguration | ./data/borgmatic.d nach /etc/borgmatic.d |
| Repository | ./data/repository nach /mnt/borg-repository |
| Quelldaten | /srv/daten nach /mnt/source, nur lesend |
| Geheimnisse | .env: BORG_PASSPHRASE, Datenbank-Passwörter, Ping-URL |
borgmatic passt zu Linux-Servern mit Datenbanken, die auf eigenen Speicher oder per SSH sichern. Für S3 und andere Objektspeicher sind restic oder Kopia die bessere Wahl, weil Borg 1.x solche Ziele nur über eine Zwischenschicht wie rclone mount erreicht. Nur ein Prozess schreibt gleichzeitig in ein Repository, mehrere Server sichern deshalb in je ein eigenes.
Schritt 1: Verzeichnisse anlegen
Alle Pfade müssen persistent sein, sonst verlieren Sie beim Neustart Repository, Schlüssel oder Cache. Ohne Cache-Mount baut Borg den Chunk-Index bei jedem Lauf neu auf, bei großen Repositories stundenlang.
# Basisverzeichnis fuer den Dienst anlegen
mkdir -p /opt/borgmatic
cd /opt/borgmatic
# Unterverzeichnisse fuer persistente Daten
mkdir -p data/repository
mkdir -p data/borgmatic.d
mkdir -p data/cache
mkdir -p data/config
mkdir -p data/state
mkdir -p data/ssh
# Rechte einschraenken, das Repository enthaelt alle Daten
chmod 700 data/repository
chmod 700 data/ssh
chmod 750 data/borgmatic.d
| Host-Pfad | Container-Pfad | Zweck |
|---|---|---|
./data/repository | /mnt/borg-repository | Borg-Repository mit allen Archiven |
./data/borgmatic.d | /etc/borgmatic.d | Konfiguration, alle YAML-Dateien darin werden gelesen |
./data/cache | /root/.cache/borg | Chunk-Index, beschleunigt Folgeläufe |
./data/config | /root/.config/borg | Borg-Konfiguration und Keyfiles, laut README mitsichern |
./data/state | /root/.local/state/borgmatic | Zeitstempel der Prüfläufe für die Frequenzsteuerung |
./data/ssh | /root/.ssh | Schlüssel für entfernte Repositories |
Verifizieren: ls -ld /opt/borgmatic/data/* zeigt sechs Verzeichnisse, repository und ssh mit drwx------.
Schritt 2: compose.yaml und .env anlegen
Docker Compose liest die .env nur für Platzhalter in der compose.yaml. In den Container gelangt eine Variable erst, wenn sie unter environment steht. Weil die borgmatic-Konfiguration in Schritt 3 neben der Passphrase auch Datenbank-Passwörter, Ping-URL und ntfy-Thema als ${…} liest, reicht die Datei diese Werte ebenfalls durch.
services:
borgmatic:
# feste Version: borgmatic 2.2.0 mit borg 1.4.5 (getestet)
image: ghcr.io/borgmatic-collective/borgmatic:2.2.0
container_name: borgmatic
hostname: borgmatic
environment:
- TZ=Europe/Berlin
# Passphrase niemals hier eintragen, sondern aus der .env ziehen
- BORG_PASSPHRASE=${BORG_PASSPHRASE}
# Variablen, die die borgmatic-Konfiguration per ${...} liest
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
- BORG_HEALTHCHECK_URL=${BORG_HEALTHCHECK_URL}
- NTFY_TOPIC=${NTFY_TOPIC}
# taeglich um 02:00 Uhr Containerzeit
- CRON=0 2 * * *
- CRON_COMMAND=borgmatic-start --stats -v 0 2>&1
volumes:
# Quelldaten ausschliesslich lesend einbinden
- /srv/daten:/mnt/source:ro
- ./data/repository:/mnt/borg-repository
- ./data/borgmatic.d:/etc/borgmatic.d
- ./data/cache:/root/.cache/borg
- ./data/config:/root/.config/borg
- ./data/state:/root/.local/state/borgmatic
- ./data/ssh:/root/.ssh
# Zielverzeichnis fuer Wiederherstellungstests
- ./restore:/mnt/restore
restart: unless-stopped
# genug Zeit, damit ein laufendes Backup sauber endet
stop_grace_period: 10m
Ohne stop_grace_period: 10m beendet Docker den Container nach zehn Sekunden hart, ein laufendes Backup hinterlässt dann eine Repository-Sperre. Die Passphrase gehört weder in die compose.yaml noch in die borgmatic-Konfiguration, weil beide oft versioniert werden:
# Datei: /opt/borgmatic/.env
# Diese Datei niemals in ein Git-Repository einchecken
BORG_PASSPHRASE=HIER-EINE-LANGE-ZUFAELLIGE-PASSPHRASE-EINSETZEN
POSTGRES_PASSWORD=PLATZHALTER-DB-PASSWORT
MYSQL_ROOT_PASSWORD=PLATZHALTER-DB-PASSWORT
BORG_HEALTHCHECK_URL=https://hc-ping.com/BEISPIEL-UUID-HIER-EINSETZEN
NTFY_TOPIC=backup-meldungen-eindeutiger-name
# Rechte auf die Geheimnisdatei strikt setzen
chmod 600 /opt/borgmatic/.env
# Eine belastbare Passphrase erzeugen
openssl rand -base64 32
Bewahren Sie die Passphrase zusätzlich im Passwortmanager und auf Papier auf. Ohne Passphrase und Schlüssel sind die Daten endgültig verloren.
Verifizieren: docker compose config zeigt unter environment alle fünf Variablen mit Werten statt leerer Einträge.
Schritt 3: borgmatic-Konfiguration schreiben
Die Datei liegt auf dem Host unter ./data/borgmatic.d/config.yaml. borgmatic liest alle YAML-Dateien im Verzeichnis, mehrere Backup-Sätze sind also möglich. Tragen Sie Container-Pfade ein, keine Host-Pfade.
# Welche Verzeichnisse gesichert werden
source_directories:
- /mnt/source
# Wohin gesichert wird, mehrere Eintraege sind moeglich
repositories:
- path: /mnt/borg-repository
label: lokal
# Feste Archivnamen, weil Container-Hostnamen wechseln koennen
archive_name_format: 'srv01-{now:%Y-%m-%d-%H%M%S}'
# Was nicht ins Backup gehoert
exclude_patterns:
- '*/node_modules'
- '*/.cache'
- '*.tmp'
# Kompression, zstd Stufe 3 ist ein guter Kompromiss
compression: zstd,3
# Nicht ueber Dateisystemgrenzen hinweg sichern
one_file_system: true
# Aufbewahrung, greift beim Befehl prune
keep_daily: 7
keep_weekly: 4
keep_monthly: 6
keep_yearly: 1
# Konsistenzpruefungen mit eigener Frequenz
checks:
- name: repository
frequency: 2 weeks
- name: archives
frequency: 1 month
# Datenbank-Dumps vor dem Backup, direkt zu Borg gestreamt
postgresql_databases:
- name: kundendb
hostname: postgres
username: postgres
password: ${POSTGRES_PASSWORD}
format: custom
mysql_databases:
- name: shopdb
hostname: mariadb
username: root
password: ${MYSQL_ROOT_PASSWORD}
# Hooks vor und nach dem Lauf
commands:
- before: everything
run:
- echo "Backup startet"
- after: everything
states: [finish]
run:
- echo "Backup erfolgreich"
# Monitoring per Healthchecks.io
healthchecks:
ping_url: ${BORG_HEALTHCHECK_URL}
# Zusaetzliche Push-Meldung bei Fehlern per ntfy
ntfy:
topic: ${NTFY_TOPIC}
server: https://ntfy.sh
fail:
title: Backup fehlgeschlagen
message: borgmatic meldet einen Fehler
priority: high
Ohne festes archive_name_format findet Pruning alte Archive nicht mehr, wenn der Container-Hostname wechselt. keep_* wirkt erst bei prune, die Prüfung archives liest alle Daten und dauert entsprechend lange.
Die Datenbank-Hooks setzen voraus, dass die Container postgres und mariadb im selben Docker-Netzwerk erreichbar sind. Ohne Datenbanken entfernen Sie beide Abschnitte. Im Test mit postgres:17-alpine und mariadb:11.8 liefen beide Dumps.
Verifizieren: docker compose exec borgmatic borgmatic config validate meldet All configuration files are valid. Diese Prüfung löst ${…}-Variablen nicht auf. Ob alle Werte im Container ankommen, zeigt erst der erste Lauf in Schritt 4.
Schritt 4: Starten, initialisieren und erstes Backup
# Container im Hintergrund starten
cd /opt/borgmatic
docker compose up -d
# Laufenden Zustand pruefen
docker compose ps
# Startmeldungen ansehen
docker compose logs --tail=30
# Versionen im Container feststellen
docker compose exec borgmatic borgmatic --version
docker compose exec borgmatic borg --version
Danach initialisieren Sie das Repository einmalig und erzeugen das erste Archiv:
# Repository einmalig initialisieren
docker compose exec borgmatic borgmatic init --encryption repokey
# Erstes Backup mit Statistik erzeugen
docker compose exec borgmatic borgmatic create --verbosity 1 --stats
# Vorhandene Archive auflisten
docker compose exec borgmatic borgmatic list
# Details zum Repository und zum letzten Archiv
docker compose exec borgmatic borgmatic info
repokey legt den Schlüssel im Repository ab, üblich für eigenen Speicher. Bei fremden Anbietern ist keyfile besser, der Schlüssel liegt dann nur in ./data/config. Neuere borgmatic-Versionen nennen die Aktion repo-create, init funktionierte im Test weiterhin. Exportieren Sie den Schlüssel sofort, denn bei repokey ist er mit einem beschädigten Repository ebenfalls weg:
# Schluessel exportieren
# und die Dateien anschliessend vom Server auf ein anderes Medium kopieren
docker compose exec borgmatic borg key export /mnt/borg-repository /root/.ssh/borg-key-backup
# Zusaetzlich als ausdruckbare Papierfassung
docker compose exec borgmatic borg key export --paper /mnt/borg-repository /root/.ssh/borg-key-papier.txt
Verifizieren: docker compose ps zeigt Up, die Versionsabfragen liefern 2.2.0 und borg 1.4.5. Der Lauf endet mit Successfully ran configuration file, borgmatic list zeigt ein Archiv srv01-…, und unter data/ssh liegen beide Schlüsseldateien.
Schritt 5: Prüfen und alte Archive entfernen
# Konsistenzpruefung ausfuehren
docker compose exec borgmatic borgmatic check --verbosity 1
# Aufbewahrungsregeln anwenden und alte Archive entfernen
docker compose exec borgmatic borgmatic prune --verbosity 1 --stats
# Freigewordenen Platz tatsaechlich zurueckgeben
docker compose exec borgmatic borgmatic compact
prune entfernt nur Archiveinträge, Plattenplatz gibt erst compact frei. Im Test ergab prune mit drei Archiven eines Tages Keeping 2 archives …, pruning 1 archives: keep_daily behält je Kalendertag nur das jüngste Archiv.
Verifizieren: check meldet Finished full repository check, no problems found und Archive consistency check complete, no problems found.
Schritt 6: Wiederherstellung prüfen
Holen Sie das jüngste Archiv vollständig in ein leeres Zielverzeichnis:
# Komplettes juengstes Archiv in ein leeres Verzeichnis zurueckholen
docker compose exec borgmatic borgmatic extract --archive latest --destination /mnt/restore --verbosity 1
Verwenden Sie latest oder den exakten Archivnamen. Im Test enthielt ein per Skript aus borgmatic list gelesener Name die Repository-Bezeichnung und führte zu Archive ... does not exist (Exit 31). Die Dateien landen mit ihrem Archivpfad unter /mnt/restore/mnt/source/ und gehören root. Vergleichen Sie deshalb die Prüfsummen im Container:
# Pruefsummen der Originale vor dem Backup notieren
cd /srv/daten && sha256sum rechnung.txt app.conf
# Nach der Wiederherstellung im Container vergleichen
docker compose exec borgmatic sh -c 'cd /mnt/restore/mnt/source && sha256sum rechnung.txt app.conf'
Eine einzelne Datei holen Sie ohne führenden Schrägstrich zurück, weil Borg Pfade im Archiv relativ speichert:
# Nur eine einzelne Datei zurueckholen
# Der Pfad wird ohne fuehrenden Schraegstrich angegeben
docker compose exec borgmatic borgmatic extract --archive latest --path mnt/source/rechnung.txt --destination /mnt/restore
borgmatic restore spielt einen Datenbank-Dump aus dem Archiv ein und überschreibt dabei die Zieldatenbank. Richten Sie den Befehl deshalb zuerst auf eine Testinstanz:
# Laut offizieller Dokumentation: Datenbank aus dem juengsten Archiv einspielen
docker compose exec borgmatic borgmatic restore --archive latest --database kundendb
Planen Sie Restore-Tests regelmäßig ein, jährlich auch auf einem anderen Host mit Schlüssel und Passphrase aus der Ablage. Details: Restore-Test-Routine.
Verifizieren: Beide sha256sum-Ausgaben sind identisch. Im Test stimmten alle Testdateien überein, auch die einzeln zurückgeholte Datei.
Schritt 7: Zeitsteuerung und Monitoring
Die compose.yaml nutzt den internen Cron des Images. CRON nimmt einen Cron-Ausdruck, ohne Angabe läuft laut README täglich um 01:00 Uhr ein Backup. Verwenden Sie als Befehl borgmatic-start, sonst greift die Signalbehandlung beim Stoppen nicht.
Alternativ startet ein Host-Cron oder systemd-Timer (mit Persistent=true für verpasste Läufe) den Container nur pro Lauf. Setzen Sie dann CRON=false und entfernen Sie restart: unless-stopped, sonst laufen zwei Zeitpläne parallel.
# Alternative ohne internen Cron: Aufruf fuer Host-Cron oder systemd-Service (Type=oneshot)
cd /opt/borgmatic && /usr/bin/docker compose run --rm borgmatic borgmatic --stats -v 0
Ein Job, der nicht mehr startet, meldet nichts. Dagegen hilft eine Totmannschaltung: Healthchecks.io erwartet regelmäßig ein Lebenszeichen und alarmiert, wenn es ausbleibt. borgmatic pingt laut Dokumentation bei Start, Erfolg und Fehler. Cronitor, ntfy, Apprise und Uptime Kuma sind ebenfalls eingebaut. Den Aufbau zeigt die Anleitung zu Healthchecks für Cronjob-Überwachung.
Verifizieren: Nach dem nächsten geplanten Lauf zeigt Healthchecks.io den Check als „up“.
Schritt 8: Offsite-Kopie und Updates
Ein lokales Repository schützt nicht gegen Ransomware auf demselben Server. Tragen Sie unter repositories zusätzlich ein SSH-Ziel ein, borgmatic sichert dann nacheinander in beide. Im Append-only-Modus nimmt das Ziel neue Daten an, der Client kann aber keine Archive löschen:
# Datei auf dem Backup-Zielserver: ~/.ssh/authorized_keys
# Der Client darf schreiben, aber nichts loeschen
command="borg serve --append-only --restrict-to-path /srv/borg/srv01",restrict ssh-ed25519 AAAAC3Nza...BEISPIEL client@srv01
Das schützt gegen einen kompromittierten Client, nicht gegen Zugriff auf das Dateisystem des Zielservers. prune und compact führen Sie dann auf dem Zielserver aus. Die Anleitung zur 3-2-1-Strategie ordnet die Bausteine ein.
Für ein Update erhöhen Sie den Image-Tag in der compose.yaml, während kein Backup läuft. Neuere Tags finden Sie in der Container-Registry, lesen Sie vorher die Release Notes. Laut README gibt es zusätzlich Tags der Form 2.x.x-1.x.x für ein festes Paar aus borgmatic- und Borg-Version.
# Sicherstellen, dass gerade kein Backup laeuft (keine borg-Prozesse)
docker compose exec borgmatic ps
# Nach Aenderung des Tags neues Image holen
docker compose pull
# Container mit neuem Image neu erstellen
docker compose up -d
# Version und Funktion nach dem Update kontrollieren
docker compose exec borgmatic borg --version
docker compose exec borgmatic borgmatic check --verbosity 1
Ein Wechsel der Borg-Hauptversion ist kein Routine-Update: Borg 2 verwendet ein neues Repository-Format, das Borg 1.x nicht liest. Wechseln Sie nur geplant, mit Kopie des Repositories und nach den Migrationshinweisen der Borg-Dokumentation.
Update von 2.1.10 auf 2.2.0: Laut Release Notes bleibt die Konfiguration kompatibel. 2.2.0 bringt einen Hook tarballs, der Tar-Archive (etwa VM-Backups) mit voller Deduplizierung ihres Inhalts sichert, behebt unter anderem ein Hängen bei Datenbank-Dumps mit viel Ausgabe sowie fehlende Cache-Treffer der ZFS-, LVM- und Btrfs-Hooks und tauscht im vorkompilierten Programm vertrauenswürdige Zertifikate der kompromittierten Zertifizierungsstelle eMudhra aus. Python 3.10 wird nicht mehr unterstützt; das betrifft nur Installationen außerhalb des Containers. Im Test am 31.10.2026 lief das Update von 2.1.10 auf 2.2.0 mit derselben compose.yaml: Das bestehende Archiv blieb sichtbar, ein weiteres Backup und check liefen mit Successfully ran configuration file durch, die Wiederherstellung lieferte identische Prüfsummen, und der Cron-Eintrag aus CRON war unverändert.
Verifizieren: borg --version zeigt nach dem Update weiterhin borg 1.x, check meldet keine Probleme.
Grundlagen zum Betrieb finden Sie in Docker Compose unter Linux: Grundlage für Selfhosting, Docker Compose absichern: Secrets, Healthchecks, Non-Root, Docker-Container automatisch aktualisieren: Diun, WUD und Renovate im Vergleich und Linux-Server absichern mit UFW und Fail2ban.
Typische Fehler
| Symptom | Ursache | Lösung |
|---|---|---|
Failed to create/acquire the lock | Vorheriger Lauf wurde hart abgebrochen | Nach Prüfung, dass kein Prozess läuft: borgmatic break-lock |
Cannot acquire a passphrase | Passphrase nicht in der Umgebung des Containers, im Test mit per exec gesetztem BORG_PASSPHRASE_FILE reproduziert | BORG_PASSPHRASE oder BORG_PASSPHRASE_FILE in der compose.yaml setzen, für Einzelaufrufe BORG_PASSCOMMAND |
Cannot find variable … in environment | Die Konfiguration nutzt ${…}, die Variable steht aber nur in der .env und nicht unter environment | Variable in der compose.yaml durchreichen, Container mit docker compose up -d neu erstellen |
Archive ... does not exist, Exit 31 | Falsch ermittelter Archivname, im Test reproduziert | --archive latest nutzen oder Namen exakt übernehmen |
Runtime Error: FUSE mount failed, Exit 2 | /dev/fuse im Container nicht verfügbar, im Test reproduziert | extract verwenden oder laut README Gerät /dev/fuse, SYS_ADMIN und apparmor:unconfined in einer eigenen docker-compose.restore.yml ergänzen |
| Wiederhergestellte Dateien nicht lesbar | Extrahierte Dateien gehören root, im Test beobachtet | Im Container prüfen oder Eigentümer anschließend korrigieren |
| Pruning entfernt nichts | Archivname enthält wechselnden Hostnamen | Festes archive_name_format setzen |
| Repository schrumpft trotz prune nicht | compact wurde nie ausgeführt | borgmatic compact regelmäßig einplanen |
Healthchecks error: 400 Client Error: Bad Request | BORG_HEALTHCHECK_URL enthält noch den Platzhalter, im Test reproduziert; das Backup läuft weiter | Echte Ping-URL eintragen, docker compose up -d |
Im Test funktionierte für einen einzelnen Aufruf das Lesen der Passphrase aus einer Datei über BORG_PASSCOMMAND. Mit --verbosity 2 zeigt borgmatic die abgesetzten Borg-Befehle:
# Selbst geprueft: Passphrase aus einer Datei ueber BORG_PASSCOMMAND lesen
# Die Datei liegt mit Rechten 600 ausserhalb des Konfigurationsverzeichnisses
docker compose exec borgmatic env -u BORG_PASSPHRASE BORG_PASSCOMMAND="cat /root/.ssh/passphrase.txt" borgmatic list
# Maximale Ausfuehrlichkeit fuer die Fehlersuche
docker compose exec borgmatic borgmatic create --verbosity 2
Häufige Fragen
borgmatic oder restic?
borgmatic für Linux-Server mit Datenbanken und eigenem oder SSH-Ziel, restic für S3-Objektspeicher und mehrere gleichzeitig schreibende Clients.
Wie entfernen Sie borgmatic wieder?
docker compose down entfernt nur den Container, die Daten bleiben. Die Dateien unter data gehören root, löschen Sie sie mit sudo und nur, wenn die Sicherungen an anderer Stelle vorliegen und dort erfolgreich wiederhergestellt wurden. docker compose down -v löscht hier nichts, weil nur Bind-Mounts verwendet werden.
Lässt sich ein Archiv wie ein Laufwerk einhängen?
Ja, mit borgmatic mount. Der Container braucht dafür FUSE und SYS_ADMIN, was seine Rechte erheblich erweitert. Für gelegentliche Wiederherstellungen ist extract die sicherere Wahl.
Fazit
Mit borgmatic im Container wird die Backup-Logik eines Linux-Servers zu einer versionierbaren YAML-Datei. Im Test liefen Initialisierung, Backup, Prüfung, Aufbewahrung und Wiederherstellung mit identischen Prüfsummen. Entscheidend für den Betrieb sind eine fest gepinnte Version, die Passphrase getrennt vom Server, eine Offsite-Kopie im Append-only-Modus und Restore-Tests als feste Routine.


