Beispielimplementation: Borg Backup mit Hilfe von docker (compose)
- Docker-Container-Up-to-Date-Security: Mir gefällt bei den Borg Backups (im Internet) via Docker oft nicht, dass irgendwelche docker-container verwendet werden, daher baue ich mein Image selbst aufbauend auf einem vertrauten Image (debian:stable-slim).
- Regelmäßigkeit Außerdem: man braucht Borg ja eigentlich nicht ständig, aber regelmäßig (über einen scheduler wie cron. Man kann jetzt einen docker-container bauen, der ständig läuft und cron enthält. Mag ich auch nicht. Ich nehme dafür cron auf dem System.
- docker vs docker compose Außerdem: eigentlich braucht man docker-compose dafür nicht. Es reicht ja ein
docker -v /srv/backup:/srv/target exec|run...
usw. Aber ich mag dann doch lieber docker-compose.yml, selbst wenn der Dockercontainer ein Zombie ist.
- Flexibilität und Dokumentation: Das Elegante an einer Lösung über docker compose: ich kann beliebige „hooks“ noch einfügen, wenn ich mag, z.B. wenn man einen datenbank-container dumpen will, wenn man container anhalten mag, wenn man lokale Backupstrategien nutzen will (z.B. bei mailcow/helper-scripts/backup_and_restore.sh). Ich habe (außer der cron-links) alle Config in der Nähe des docker-compose.yml. Da kann ich mich noch später erinnern, wie es funktioniert:
Beispielhafte docker-struktur:
/srv/docker/app1
/srv/docker/app2
+- app2/grossercache
/srv/docker/borgbackup
+- borgbackup/Dockerfile
+- borgbackup/borgbackup.sh
+- borgbackup/borg-localbackup-cron
/srv/docker/docker-compose.yml
/etc/nginx/
backup-target (lokal)
Ums einfach zu halten, gibt es jetzt nur ein lokales Backup auf dem docker-host nach /srv/backup
, aber das kann auch ein remote Backup sein…
Dafür muss das Target in den docker-container des borgbackups gemountet werden. Bei einem remote backup müsste man stattdessen noch die ssh-key Geschichte implementieren.
/srv/backup -> borgbackup-container (/srv/target)
backup-quellen
- sind z.B. die verschiedenen docker-apps, aber auch alles was ich sonst noch will. Die müssen natürlich auch im docker-container als volumes auftauchen
/srv/docker/app1 -> borgbackup-container (/source/app1)
...
/etc/nginx -> borgbackup-container (/source/nginx)
borg-backup container
borgbackup/Dockerfile
:
FROM debian:stable-slim
RUN apt update && apt full-upgrade -y && apt install -y borgbackup && rm -rf /var/lib/apt/lists/*
docker-compose.yml
(kein version: mehr für neueres docker compose)
services:
## app1
app1:
image: app/app1
...
## app2
app2:
image: app/app2
volumes:
- ./app2/grossercache:/cache
## Borg Backup
## docs: https://borgbackup.readthedocs.io/en/1.2-maint/quickstart.html
borgbackup:
image: linuxmuster/borgbackup:latest
build:
dockerfile: Dockerfile
context: ./borgbackup
container_name: borgbackup
command: tail -f /dev/null
restart: always
volumes:
- /srv/backup:/srv/target
- ./borgbackup/borgbackup.sh:/borgbackup.sh
- ./app1:/source/app1:ro
- ./app2:/source/app2:ro
- /etc/nginx:/source/nginx:ro
- ./borgbackup:/source/borgbackup:ro
environment:
BORG_PASSPHRASE: 'XYZl0ngandsecurepa_55_phrasea&&123'
BORG_REPO: /srv/target
BORG_DISPLAY_PASSPHRASE:
X_BORG_REPONAME: meinserver
X_BORG_EXCLUDES: -e */app2/grossercache
X_BORG_PATHS: "/source/app1 /source/app2 /source/borgbackup"
#oder:
#X_BORGBACKUPCMD: borg create --stats --progress --compression lz4 --verbose -e '*/app2/grossercache' "::meinserver-{now}" /source/app1 /source/app2 /source/borgbackup /source/nginx
container erstellen/update + starten/erneuern
docker compose build
docker compose up -d borgbackup
borg backup testen
docker compose run borgbackup borg help
borg backup initialisieren
docker compose run borgbackup borg init -e repokey
borg backup manuell starten / auflisten /etc.
docker compose run borgbackup borg create --verbose '::testarchivname' /source/app1
docker compose run borgbackup borg list ::testarchivname
usw...
automatisierung über borgbackup.sh
borgbackup/borgbackup.sh
:
Eigentlich Copy+Paste von Quick Start — Borg - Deduplicating Archiver 1.2.9.dev5 documentation mit nur ein paar Änderungen beim Befehl „create“ und „prune“.
#!/bin/sh
#eval $X_BORGBACKUPCMD
#exit 0
# see https://borgbackup.readthedocs.io/en/1.2-maint/quickstart.html#automating-backups
# some helpers and error handling:
info() { printf "\n%s %s\n\n" "$( date )" "$*" >&2; }
trap 'echo $( date ) Backup interrupted >&2; exit 2' INT TERM
info "Starting backup"
borg create \
--stats --progress \
--verbose \
--compression lz4 \
$X_BORG_EXCLUDES \
"::${X_BORG_REPONAME}-{now}" \
$X_BORG_PATHS
backup_exit=$?
<snip>
--glob-archives "${X_BORG_REPONAME}-*" \
<snip>
Wer will, kann auch statt des Scripts einen kompletten borg-Befehl in eine ENV-Variable wie z.B. X_BORGBACKUPCMD
in die docker-compose.yml packen und diesen dann „eval“ ausführen, siehe auskommentierte Versionen oben.
regelmäßiges Ausführen via cronjob
borgbackup/borg-localbackup-cron
#!/bin/bash
set -e
cd /srv/docker
docker compose run borgbackup /borgbackup.sh
und das verlinkt man z.B. hier:
cd /etc/cron.daily/
ln -s /srv/docker/borgbackup/borg-localbackup-cron
TODO
Viel Spaß damit, wer darauf Bock hat.
P.S. bei mir (privat) macht dieses Setup jetzt Backups für
- mein Photo-Repo: immich,
- meinen Mailserver: mailcow,
- meine Nextcloud: nextcloud/data + sql
- meine nginx-reverse-proxy-konfig (inkl. letsencrypt) und natürlich borgbackup selbst
edit 1: cron-jobs dürfen keine Endung haben (kein „.“ im Namen).