Duplicati selbst hosten: Verschlüsselte Backups mit Web-UI
Backups machen ist nervig – bis der Moment kommt, in dem man sie braucht. Duplicati automatisiert das sichere Weglegen deiner Daten: verschlüsselt, inkrementell und komprimiert – auf lokale Laufwerke genauso wie in Cloud-Speicher. Die Bedienung läuft komplett über eine Web-Oberfläche. In diesem Tutorial richtest du Duplicati mit Docker Compose ein.
Warum Duplicati?
Duplicati ist ein ausgereifter Open-Source-Backup-Client mit Web-Oberfläche: Er erstellt verschlüsselte, inkrementelle und komprimierte Sicherungen – lokal auf ein Laufwerk oder remote über FTP, SSH/WebDAV sowie Cloud-Dienste wie S3, B2, OneDrive, Google Drive oder Mega. Die LinuxServer.io-Variante bringt das Ganze als pflegeleichten Docker-Container mit fester Verzeichnisstruktur.
Konzept: verschlüsselt & inkrementell
- Verschlüsselung: Jede Sicherung wird mit einem eigenen Passwort/Schlüssel (AES) verschlüsselt – nur wer es kennt, kann die Backups lesen.
- Inkrementell & dedupliziert: Nur geänderte Blöcke wandern ins Ziel – spart Speicher und Bandbreite bei jedem Lauf.
- Ziele: Lokale Ordner (
/backups), Netzwerkfreigaben (FTP/SSH/WebDAV) und gängige Clouds – vieles direkt aus der Oberfläche konfigurierbar. - Web-UI: Sicherungen planen, starten, prüfen und wiederherstellen – ganz ohne CLI-Wissen.
- Versionen & Restore: Ältere Zustände bleiben erhalten; Wiederherstellungen laufen über denselben Assistenten.
Vorbereitung: Rechte & Ordner
mkdir -p ~/duplicati/appdata/config ~/duplicati/backups
cd ~/duplicati
./appdata/config→/config: Konfiguration und Datenbanken von Duplicati../backups→/backups: Ziel für lokale Sicherungen (z. B.file:///backups/…)./:/source: macht den kompletten Host lesbar – nur nötig, wenn wirklich alles gesichert werden soll.
PUID=0/PGID=0 – das ist die
Konsequenz aus dem /-Mount: Nur Root kann das gesamte Dateisystem lesen. Damit hat der Container
im Fehlerfall vollen Host-Zugriff. Wer gezielter sichert, mountet nur die nötigen Ordner und setzt
PUID/PGID auf den Host-User (z. B. 1000:1000).
Docker Compose – die Datei
Deine Vorlage, bereinigt (leerer networks: {}-Block entfernt) und kommentiert:
services:
duplicati:
image: lscr.io/linuxserver/duplicati:latest
container_name: duplicati
restart: unless-stopped
environment:
- PUID=0 # Root – nötig für den /-Mount
- PGID=0
- TZ=Europe/Berlin
- CLI_ARGS= # optional: zusätzliche Argumente
- DUPLICATI__WEBSERVICE_PASSWORD=MeinWEBSERVICEPASSWORD1234# # Web-UI-Passwort
- SETTINGS_ENCRYPTION_KEY=MeinENCRYPTIONKEY1234# # verschlüsselt die Server-Einstellungen
volumes:
- ./appdata/config:/config # Konfiguration & Datenbanken
- ./backups:/backups # Ziel für lokale Backups
- /:/source # komplette Host-Festplatte als Quelle
ports:
# Host-Port : Container-Port
- "127.0.0.1:50521:8200" # Web-UI nur lokal, Proxy macht den Rest
lscr.io/linuxserver/duplicati:latest: Stable-Zweig;:developmententhält Beta-Versionen.127.0.0.1:50521:8200: Duplicati-Web-UI auf Port 8200 im Container.- Das
/-Mount sollte so eng wie möglich bleiben – lieber gezielte Pfade wie/etc,/homeoder/opt/stackseinbinden.
Konfiguration: Passwörter & Schlüssel
| Variable | Erklärung |
|---|---|
DUPLICATI__WEBSERVICE_PASSWORD | Setzt das Passwort der Web-Oberfläche direkt beim Start – kein interaktiver Ersteinrichtungs-Dialog nötig. |
SETTINGS_ENCRYPTION_KEY | Verschlüsselt die lokalen Server-Einstellungen/Datenbanken auf der Platte (AES) – schützt die Konfiguration, nicht die Backups selbst. |
CLI_ARGS | Optionale zusätzliche Startargumente für Duplicati (z. B. angepasste Pfade) – in der Regel leer. |
PUID/PGID | Nutzer/Gruppe, unter der der Prozess läuft – siehe Warnbox oben (hier bewusst 0 wegen des /-Mounts). |
TZ | Zeitzone des Containers – wichtig für korrekte Zeitstempel in Logs und Planung. |
.env-Datei ab (chmod 600) und referenziere sie per ${VAR}.
Ohne SETTINGS_ENCRYPTION_KEY kann Duplicati seine lokale Konfiguration nicht mehr entschlüsseln –
beides gehört in den Passwortmanager. Die Backup-Passwörter der einzelnen Sicherungen sind davon unabhängig.
Reverse Proxy
Wie bei den anderen Tutorials binden wir die Oberfläche nur an localhost und stellen sie per TLS-Proxy bereit. Die Upgrade-Header schaden nicht und decken ab, falls die UI Verbindungen über WebSockets aufbaut:
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name dc.meine.domain;
access_log off;
error_log /var/log/nginx/dc.meine.domain.error.log;
# IP-Bereiche erlauben
# TIPP: Nutze hier das /24 Subnetz, falls Docker die IP des Containers beim Update ändert
# allow 10.42.42.0/24;
# allow 10.42.42.41;
# allow 127.0.0.1;
# allow ::1;
# allow 10.11.0.0/24;
# allow 10.8.0.0/24;
# allow 10.0.0.0/24;
# Alle anderen blockieren
# deny all;
# ---------------------------------------------------------------- Zertifikat (ECDSA)
ssl_certificate /etc/ssl/private/dc.meine.domain_ecc/fullchain.cer;
ssl_certificate_key /etc/ssl/private/dc.meine.domain_ecc/dc.meine.domain.key;
# ---------------------------------------------------------------- TLS-Feinschliff
ssl_buffer_size 1400; # passt gut zu 1500-Byte-Ethernet-MTU
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off; # Session-Tickets aus: bessere Forward Secrecy
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off; # TLS 1.3 entscheidet der Client; aktueller Rat
ssl_stapling on;
ssl_stapling_verify on;
ssl_ecdh_curve X25519:P-384:P-256:P-521;
# OPTIMIERUNG 2: Lokalen Resolver (oder Quad9/Cloudflare) eintragen für besseren Datenschutz als Google
resolver 9.9.9.9 1.1.1.1 valid=300s;
resolver_timeout 5s;
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains; preload" always;
add_header X-Xss-Protection "1; mode=block" always;
add_header X-Content-Type-Options nosniff always;
# ERGÄNZUNG 1: Schützt deine Duplicati-Oberfläche davor, ungefragt in fremde iFrames eingebunden zu werden
add_header X-Frame-Options "SAMEORIGIN" always;
location / {
# Standard-Proxy-Header
proxy_read_timeout 300s;
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WICHTIG: WebSocket-Unterstützung für die Duplicati-UI
proxy_set_header Upgrade $http_upgrade;
# KORREKTUR 1: Dynamischer Verbindungs-Header für korrekte WebSocket-Handshakes
proxy_set_header Connection "upgrade";
# Verhindert fehlerhafte Weiterleitungen der Duplicati-UI
proxy_redirect off;
proxy_pass http://127.0.0.1:50521;
}
}
DUPLICATI__WEBSERVICE_PASSWORD ist Pflicht, wenn die UI nicht nur im
lokalen Netz hängt.
Erster Start & erste Sicherung
cd ~/duplicati
docker compose up -d
docker compose logs -f duplicati
Danach https://dc.meine.domain öffnen:
- Anmeldung mit dem Passwort aus
DUPLICATI__WEBSERVICE_PASSWORD. - Unter „Add backup“ eine erste Sicherung anlegen: Quelle z. B.
/config(die eigene Konfiguration!) oder/opt/stacks. - Als Ziel lokal
/backups/mein-serverwählen oder ein Cloud-Ziel über den Assistenten einrichten. - Ein eigenes Backup-Passwort vergeben (AES) – es schützt die Daten im Ziel.
- Ersten Lauf starten und anschließend den Wiederherstellungs-Assistenten mit einer Testdatei durchspielen.
Backup des Backups: Restore-Tests
Ein Backup, das nie wiederhergestellt wurde, ist nur eine Hoffnung. Plane deshalb regelmäßige
Restore-Tests ein: Lege eine Test-Sicherung an, stelle sie in einen leeren Ordner wieder her und prüfe
die Dateien. Die Konfiguration selbst sicherst du am einfachsten mit, indem /config Teil
einer Sicherung ist.
docker compose pull && docker compose up -d.
SETTINGS_ENCRYPTION_KEY und Web-Passwort sicher aufbewahren (Passwortmanager).
Nach jedem großen Update einmal die geplanten Sicherungen beobachten.
Häufige Probleme (FAQ)
| Problem | Lösung |
|---|---|
| Web-UI fragt trotz Env-Variable ein Passwort ab | Container neu erstellen (docker compose up -d --force-recreate); Wert in der .env prüfen (Sonderzeichen wie # quoten). |
| Backup bricht mit „keine Berechtigung“ ab | Bei /-Quelle Root nötig (PUID/PGID=0); bei gezielten Mounts gehört der Ordner dem gesetzten User. |
| „Settings cannot be decrypted“ / Konfiguration weg | SETTINGS_ENCRYPTION_KEY stimmt nicht (mehr) – Schlüssel aus dem Passwortmanager wiederherstellen. |
| Ziel nicht erreichbar (S3/Cloud) | Zugangsdaten und Region prüfen; erste Verbindung am besten über die Ziel-Assistenten testen. |
| Sehr große Sicherungen dauern lange | Inkrementelle Läufe sind nach dem ersten Voll-Backup deutlich schneller; ggf. Zeitfenster außerhalb der Stoßzeiten wählen. |
| Wiederherstellung liefert kaputte Dateien | Backup-Volumes prüfen (Verification), Ziel-Laufwerk testen und Restore an eine andere Stelle probieren. |
Fazit
Duplicati nimmt dir die Ausrede: verschlüsselte, inkrementelle Sicherungen mit Web-UI – lokal oder in die Cloud – sind in einer halben Stunde eingerichtet. Der Preis dafür ist Verantwortung: Schlüssel und Passwörter sicher verwahren und Restore-Tests regelmäßig durchspielen. Dann hält das Backup, wenn es darauf ankommt.
SETTINGS_ENCRYPTION_KEY keine entschlüsselbare Konfiguration.
③ Jedes Backup braucht ein eigenes, sicheres Passwort.
④ Ein Backup ist erst ein Backup, wenn der Restore-Test bestanden wurde.