Seafile 13 mit Docker Compose installieren, absichern und sichern
Seafile Community Edition 13.0.28 per Docker Compose auf einem Server mit 3 GB RAM: Installation mit Caddy, typische Fehler wie falsches Protokoll oder DB-Passwort, Backup mit Datenbank zuerst und ein nachgewiesener Restore.
Geprüft am 05.10.2026 · für Seafile CE 13.0.28
Mit KI erstellt – redaktionelle Prüfung ausstehend
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

Seafile ist ein quelloffener Dienst für Dateisynchronisation und Cloud-Speicher. Dateien liegen in Bibliotheken; die Weboberfläche dient Freigaben und Verwaltung. Diese Anleitung installiert die Community Edition 13.0.28 mit dem offiziellen Docker-Stack (Seafile, MariaDB, Redis, SeaDoc und Caddy), prüft Upload, Download und Persistenz, sichert den Zugang ab und zeigt ein Backup samt Restore, das mit Marker-Dateien nachgewiesen wurde.
Voraussetzungen
- Server: Linux x86_64 oder ARM64 (das Image gibt es für amd64 und arm64) mit Docker Engine und Compose-Plugin v2, mindestens 2 CPU-Kerne.
- Arbeitsspeicher: die Doku nennt 2 GB. Im Test belegten alle fünf Container zusammen im Leerlauf rund 750 MiB (Seafile 514, MariaDB 146, SeaDoc 74, Caddy 15, Redis 4).
- Speicher: laut Doku mindestens 10 GB frei, empfohlen mehr als 50 GB; das Backup gehört auf ein zweites Medium.
- Netz: ein DNS-Name mit A-Record auf den Server, offene Ports 80 und 443 (Caddy holt das Zertifikat per Let’s Encrypt).
Schritt 1: Verzeichnis anlegen und Stack laden
Die Dateien kommen direkt von der Seafile-Dokumentation für Version 13.0. Das Compose-Projekt liegt in /opt/seafile, die Daten in /opt/seafile-data und /opt/seafile-mysql.
sudo mkdir -p /opt/seafile
cd /opt/seafile
sudo wget -O .env https://manual.seafile.com/13.0/repo/docker/ce/env
sudo wget https://manual.seafile.com/13.0/repo/docker/ce/seafile-server.yml
sudo wget https://manual.seafile.com/13.0/repo/docker/seadoc.yml
sudo wget https://manual.seafile.com/13.0/repo/docker/caddy.yml
seafile-server.yml enthält die Dienste db, redis und seafile. Das Image seafileltd/seafile-mc:13.0-latest zeigte zum Testzeitpunkt auf denselben Digest wie das Tag 13.0.28. Für feste Versionen tragen Sie in .env das exakte Tag ein.
services:
db:
image: ${SEAFILE_DB_IMAGE:-mariadb:10.11}
container_name: seafile-mysql
restart: unless-stopped
environment:
- MYSQL_ROOT_PASSWORD=${INIT_SEAFILE_MYSQL_ROOT_PASSWORD:-}
- MYSQL_LOG_CONSOLE=true
- MARIADB_AUTO_UPGRADE=1
volumes:
- "${SEAFILE_MYSQL_VOLUME:-/opt/seafile-mysql/db}:/var/lib/mysql"
networks:
- seafile-net
healthcheck:
test:
[
"CMD",
"/usr/local/bin/healthcheck.sh",
"--connect",
"--mariadbupgrade",
"--innodb_initialized",
]
interval: 20s
start_period: 30s
timeout: 5s
retries: 10
redis:
image: ${SEAFILE_REDIS_IMAGE:-redis}
container_name: seafile-redis
restart: unless-stopped
command:
- /bin/sh
- -c
- exec redis-server --requirepass "$$REDIS_PASSWORD" --save "" --appendonly no
environment:
- REDIS_PASSWORD=${REDIS_PASSWORD:-}
networks:
- seafile-net
seafile:
image: ${SEAFILE_IMAGE:-seafileltd/seafile-mc:13.0-latest}
container_name: seafile
restart: unless-stopped
# ports:
# - "80:80"
volumes:
- ${SEAFILE_VOLUME:-/opt/seafile-data}:/shared
environment:
- SEAFILE_MYSQL_DB_HOST=${SEAFILE_MYSQL_DB_HOST:-db}
- SEAFILE_MYSQL_DB_PORT=${SEAFILE_MYSQL_DB_PORT:-3306}
- SEAFILE_MYSQL_DB_USER=${SEAFILE_MYSQL_DB_USER:-seafile}
- SEAFILE_MYSQL_DB_PASSWORD=${SEAFILE_MYSQL_DB_PASSWORD:?Variable is not set or empty}
- INIT_SEAFILE_MYSQL_ROOT_PASSWORD=${INIT_SEAFILE_MYSQL_ROOT_PASSWORD:-}
- SEAFILE_MYSQL_DB_CCNET_DB_NAME=${SEAFILE_MYSQL_DB_CCNET_DB_NAME:-ccnet_db}
- SEAFILE_MYSQL_DB_SEAFILE_DB_NAME=${SEAFILE_MYSQL_DB_SEAFILE_DB_NAME:-seafile_db}
- SEAFILE_MYSQL_DB_SEAHUB_DB_NAME=${SEAFILE_MYSQL_DB_SEAHUB_DB_NAME:-seahub_db}
- TIME_ZONE=${TIME_ZONE:-Etc/UTC}
- INIT_SEAFILE_ADMIN_EMAIL=${INIT_SEAFILE_ADMIN_EMAIL:-me@example.com}
- INIT_SEAFILE_ADMIN_PASSWORD=${INIT_SEAFILE_ADMIN_PASSWORD:-asecret}
- SEAFILE_SERVER_HOSTNAME=${SEAFILE_SERVER_HOSTNAME:?Variable is not set or empty}
- SEAFILE_SERVER_PROTOCOL=${SEAFILE_SERVER_PROTOCOL:-http}
- SITE_ROOT=${SITE_ROOT:-/}
- NON_ROOT=${NON_ROOT:-false}
- JWT_PRIVATE_KEY=${JWT_PRIVATE_KEY:?Variable is not set or empty}
- SEAFILE_LOG_TO_STDOUT=${SEAFILE_LOG_TO_STDOUT:-false}
- ENABLE_GO_FILESERVER=${ENABLE_GO_FILESERVER:-true}
- ENABLE_SEADOC=${ENABLE_SEADOC:-true}
- SEADOC_SERVER_URL=${SEAFILE_SERVER_PROTOCOL:-http}://${SEAFILE_SERVER_HOSTNAME:?Variable is not set or empty}/sdoc-server
- CACHE_PROVIDER=${CACHE_PROVIDER:-redis}
- REDIS_HOST=${REDIS_HOST:-redis}
- REDIS_PORT=${REDIS_PORT:-6379}
- REDIS_PASSWORD=${REDIS_PASSWORD:-}
- MEMCACHED_HOST=${MEMCACHED_HOST:-memcached}
- MEMCACHED_PORT=${MEMCACHED_PORT:-11211}
- ENABLE_NOTIFICATION_SERVER=${ENABLE_NOTIFICATION_SERVER:-false}
- INNER_NOTIFICATION_SERVER_URL=${INNER_NOTIFICATION_SERVER_URL:-http://notification-server:8083}
- NOTIFICATION_SERVER_URL=${NOTIFICATION_SERVER_URL:-${SEAFILE_SERVER_PROTOCOL:-http}://${SEAFILE_SERVER_HOSTNAME:?Variable is not set or empty}/notification}
- ENABLE_SEAFILE_AI=${ENABLE_SEAFILE_AI:-false}
- ENABLE_FACE_RECOGNITION=${ENABLE_FACE_RECOGNITION:-false}
- SEAFILE_AI_SERVER_URL=${SEAFILE_AI_SERVER_URL:-http://seafile-ai:8888}
- SEAFILE_AI_SECRET_KEY=${JWT_PRIVATE_KEY:?Variable is not set or empty}
- MD_FILE_COUNT_LIMIT=${MD_FILE_COUNT_LIMIT:-100000}
labels:
caddy: ${SEAFILE_SERVER_PROTOCOL:-http}://${SEAFILE_SERVER_HOSTNAME:?Variable is not set or empty}
caddy.reverse_proxy: "{{upstreams 80}}"
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:80 || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
networks:
- seafile-net
networks:
seafile-net:
name: seafile-net
caddy.yml startet Caddy als Reverse Proxy, der Hostnamen aus den Container-Labels liest und Zertifikate beschafft. Dafür bindet er den Docker-Socket ein, was einem Root-Zugriff auf den Host gleichkommt.
services:
caddy:
image: ${SEAFILE_CADDY_IMAGE:-lucaslorentz/caddy-docker-proxy:2.12-alpine}
restart: unless-stopped
container_name: seafile-caddy
ports:
- 80:80
- 443:443
environment:
- CADDY_INGRESS_NETWORKS=seafile-net
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ${SEAFILE_CADDY_VOLUME:-/opt/seafile-caddy}:/data/caddy
networks:
- seafile-net
healthcheck:
test: ["CMD-SHELL", "curl --fail http://localhost:2019/metrics || exit 1"]
start_period: 20s
interval: 20s
timeout: 5s
retries: 3
networks:
seafile-net:
name: seafile-net
Verifizieren: ls /opt/seafile zeigt .env, seafile-server.yml, seadoc.yml und caddy.yml.
Schritt 2: .env mit eigenen Werten füllen
Erzeugen Sie Zufallswerte mit openssl rand -hex 24 und tragen Sie diese ein. JWT_PRIVATE_KEY braucht laut Doku mindestens 32 Zeichen. Gekürzte .env, übrige Variablen haben Standardwerte:
COMPOSE_FILE='seafile-server.yml,caddy.yml,seadoc.yml'
COMPOSE_PATH_SEPARATOR=','
SEAFILE_IMAGE=seafileltd/seafile-mc:13.0-latest
SEAFILE_DB_IMAGE=mariadb:10.11
SEAFILE_CADDY_IMAGE=lucaslorentz/caddy-docker-proxy:2.12-alpine
SEADOC_IMAGE=seafileltd/sdoc-server:2.0-latest
BASIC_STORAGE_PATH=/opt
SEAFILE_VOLUME=$BASIC_STORAGE_PATH/seafile-data
SEAFILE_MYSQL_VOLUME=$BASIC_STORAGE_PATH/seafile-mysql/db
SEAFILE_CADDY_VOLUME=$BASIC_STORAGE_PATH/seafile-caddy
SEADOC_VOLUME=$BASIC_STORAGE_PATH/seadoc-data
SEAFILE_SERVER_HOSTNAME=seafile.example.de
SEAFILE_SERVER_PROTOCOL=https
TIME_ZONE=Europe/Berlin
JWT_PRIVATE_KEY=HIER_ZUFALLSWERT_EINTRAGEN
SEAFILE_MYSQL_DB_PASSWORD=HIER_ZUFALLSWERT_EINTRAGEN
INIT_SEAFILE_MYSQL_ROOT_PASSWORD=HIER_ZUFALLSWERT_EINTRAGEN
REDIS_PASSWORD=HIER_ZUFALLSWERT_EINTRAGEN
INIT_SEAFILE_ADMIN_EMAIL=admin@example.de
INIT_SEAFILE_ADMIN_PASSWORD=HIER_STARKES_PASSWORT_EINTRAGEN
Das Admin-Passwort aus INIT_SEAFILE_ADMIN_PASSWORD gilt nur beim ersten Start. Schützen Sie die Datei mit sudo chmod 600 .env.
Verifizieren: sudo docker compose config -q endet ohne Ausgabe. Fehlt ein Pflichtwert, bricht Compose ab: required variable JWT_PRIVATE_KEY is missing a value: Variable is not set or empty.
Schritt 3: Starten und Gesundheit prüfen
cd /opt/seafile
sudo docker compose up -d
sudo docker compose ps
sudo docker compose logs seafile --tail 20
In den ersten Sekunden liefert der Proxy 502, dann 302 auf die Anmeldeseite. Der Container seafile war im Test nach rund 20 bis 50 Sekunden healthy; im Log erscheinen Seafile server started, Successfully created seafile admin und Seahub is started.
Verifizieren: curl -s https://seafile.example.de/api2/ping/ antwortet "pong", curl -s https://seafile.example.de/api2/server-info/ liefert "version": "13.0.28".
Schritt 4: Anmelden und Version prüfen
Öffnen Sie die Adresse im Browser und melden Sie sich mit INIT_SEAFILE_ADMIN_EMAIL an. Unter „Systemverwaltung“ zeigt die Seite „Info“ die Edition und die Version.

Verifizieren: Die Anmeldung führt zur Dateiansicht mit der Standardbibliothek „Meine Bibliothek“. Ein falsches Passwort scheitert an der API mit Unable to login with provided credentials. (HTTP 400).
Schritt 5: Upload und Download prüfen
Der Funktionstest läuft über die Web-API: Token holen, Bibliothek anlegen, Datei hochladen, herunterladen und die Prüfsumme vergleichen. Ersetzen Sie Adresse und Zugangsdaten.
U=https://seafile.example.de
T=$(curl -s -d "username=admin@example.de&password=IHR_PASSWORT" $U/api2/auth-token/ | sed 's/.*"token": *"\([^"]*\)".*/\1/')
RID=$(curl -s -H "Authorization: Token $T" -d "name=Testbibliothek" $U/api2/repos/ | sed 's/.*"repo_id":"\([^"]*\)".*/\1/')
UL=$(curl -s -H "Authorization: Token $T" "$U/api2/repos/$RID/upload-link/?p=/" | tr -d '"')
echo "marker-vor-backup" > marker.txt
dd if=/dev/urandom of=blob.bin bs=1M count=5
sha256sum blob.bin
curl -s -H "Authorization: Token $T" -F file=@marker.txt -F file=@blob.bin -F parent_dir=/ "$UL?ret-json=1"
DL=$(curl -s -H "Authorization: Token $T" "$U/api2/repos/$RID/file/?p=/blob.bin&reuse=1" | tr -d '"')
curl -s -o dl.bin "$DL"
sha256sum dl.bin

Verifizieren: Der Upload antwortet mit HTTP 200 und listet beide Dateien, beide Prüfsummen sind identisch. Ohne Token antwortet /api2/repos/ mit Authentication credentials were not provided. (HTTP 403), ein erfundener Download-Link mit Access token not found (HTTP 403).
Schritt 6: HTTPS und Protokoll korrekt setzen
Caddy beantragt das Zertifikat automatisch, sobald der Hostname öffentlich auflösbar ist und Port 80 erreichbar ist; Port 80 leitet mit 308 auf HTTPS um. Entscheidend ist SEAFILE_SERVER_PROTOCOL=https in der .env. Stand dort http, während der Browser die Seite über HTTPS aufrief, scheiterte die Anmeldung mit 403 Forbidden. Im Log /opt/seafile-data/seafile/logs/seahub.log stand Origin checking failed und does not match any trusted origins. Nach der Änderung auf https und docker compose up -d (der Container wird neu angelegt) funktionierte die Anmeldung.
Mit eigenem Reverse Proxy entfernen Sie caddy.yml aus COMPOSE_FILE und veröffentlichen den Port des Dienstes seafile nur auf 127.0.0.1; die Zuordnung 8080 auf Container-Port 80 funktionierte im Test.
Verifizieren: curl -sI http://seafile.example.de liefert 308 mit HTTPS-Ziel. Ohne DNS-Eintrag zeigt docker compose logs caddy: no valid A records found for seafile.example.de; no valid AAAA records found.
Schritt 7: Zugang absichern
Der Registrierungslink /accounts/register/ lieferte im Standardzustand 404. Unter „Systemverwaltung, Einstellungen, Benutzer“ ist „Selbstregistrierung zulassen“ ausgeschaltet; belassen Sie das so, wenn Konten nur durch Admins entstehen sollen. Solche Einstellungen speichert Seafile in der Datenbanktabelle constance_config, sie haben Vorrang vor den Konfigurationsdateien.

Redis ist mit REDIS_PASSWORD geschützt und nur im Compose-Netz erreichbar, das Datenverzeichnis gehört root (NON_ROOT=false). Ändern Sie das Admin-Passwort nach der ersten Anmeldung und halten Sie MariaDB und Redis geschlossen.
Verifizieren: sudo ss -ltn zeigt nur Port 80 und 443 (plus SSH); 3306 und 6379 tauchen nicht auf.
Schritt 8: Persistenz prüfen
Alle Daten liegen auf dem Host. Ein docker compose down mit anschließendem docker compose up -d entfernt nur Container, nicht die Verzeichnisse unter /opt.
cd /opt/seafile
sudo docker compose down
sudo docker compose up -d
Verifizieren: Nach etwa 20 Sekunden ist seafile wieder healthy, die Dateiliste enthält weiterhin blob.bin und marker.txt.
Schritt 9: Backup mit Datenbank zuerst
Die Doku sichert drei Datenbanken (ccnet_db, seafile_db, seahub_db) und das Verzeichnis seafile. Die Reihenfolge ist wichtig: Wird die Datenbank zuerst gesichert, verweist jeder Eintrag auf vorhandene Objekte; neue Objekte gehen bestenfalls verloren, Bibliotheken bleiben aber konsistent. Die .env gehört dazu, weil sie die Zugangsdaten enthält.
cd /opt/seafile
. ./.env
B=/backup/seafile
sudo mkdir -p $B/databases $B/data
for d in ccnet_db seafile_db seahub_db; do
sudo docker exec -e MYSQL_PWD="$SEAFILE_MYSQL_DB_PASSWORD" seafile-mysql mariadb-dump -useafile --opt $d | sudo tee $B/databases/$d.sql > /dev/null
done
sudo cp -a /opt/seafile-data/seafile $B/data/
sudo cp .env $B/env.bak
Verifizieren: ls -la /backup/seafile/databases zeigt drei SQL-Dateien (im Test 12, 39 und 151 KB), du -sh /backup/seafile/data/seafile etwa 5,2 MB bei der Testmenge.
Schritt 10: Restore auf eine frische Instanz
Im Test wurde der Ernstfall nachgestellt: Stack stoppen, /opt/seafile-data und /opt/seafile-mysql löschen, mit derselben .env neu starten (es entsteht eine leere Instanz ohne Bibliotheken), dann Seafile stoppen, Daten und Dumps zurückspielen und neu starten. Die Datenbank-Benutzer müssen existieren, deshalb zuerst der leere Start.
cd /opt/seafile
. ./.env
B=/backup/seafile
sudo docker compose up -d
sudo docker compose stop seafile seadoc
sudo rsync -aHAX $B/data/seafile/ /opt/seafile-data/seafile/
for d in ccnet_db seafile_db seahub_db; do
sudo docker exec -i -e MYSQL_PWD="$SEAFILE_MYSQL_DB_PASSWORD" seafile-mysql mariadb -useafile $d < $B/databases/$d.sql
done
sudo docker compose start seafile seadoc
Auf einem minimalen Host fehlte rsync (rsync: command not found); installieren Sie das Paket vorher.
Verifizieren: Nach dem Start (etwa 20 Sekunden) listet die API blob.bin und marker.txt wieder auf, der Download von blob.bin hat dieselbe SHA-256-Prüfsumme wie vor dem Backup, marker.txt enthält den ursprünglichen Text.
Schritt 11: Updates, Rollback und Entfernen
Für Updates innerhalb von 13.0 genügt laut Doku: Backup erstellen, in der .env das neue Image-Tag eintragen, docker compose pull und docker compose up -d. Bei Hauptversionssprüngen beachten Sie die Upgrade-Hinweise der Doku, die für 12.0 auf 13.0 neue .yml-Dateien und Cache-Variablen vorsehen. Ein Rollback gelingt über das Backup aus Schritt 9 und das alte Tag.
Warnung: Zum vollständigen Entfernen stoppen Sie mit docker compose down und löschen /opt/seafile-data, /opt/seafile-mysql, /opt/seafile-caddy und /opt/seadoc-data. Damit sind alle Bibliotheken unwiderruflich weg; sichern Sie vorher.
Verifizieren: docker compose pull nennt die neuen Digests, /api2/server-info/ zeigt danach die erwartete Version.
Troubleshooting
| Symptom | Ursache und Abhilfe |
|---|---|
Anmeldung liefert 403 Forbidden, Log: Origin checking failed ... does not match any trusted origins | SEAFILE_SERVER_PROTOCOL steht auf http, der Zugriff läuft über HTTPS. Auf https setzen, docker compose up -d. |
Login-Seite zeigt Page unavailable (HTTP 500), Log: Error happened during creating seafile admin. | Das DB-Passwort in der .env weicht vom beim ersten Start gesetzten ab. Ursprünglichen Wert zurücktragen und den Container neu anlegen; danach lief alles wieder. |
required variable JWT_PRIVATE_KEY is missing a value | Pflichtvariable leer oder gelöscht, in .env eintragen. |
Caddy: no valid A records found | Hostname hat keinen öffentlichen DNS-Eintrag, Zertifikat kann nicht ausgestellt werden. |
502 direkt nach dem Start | Seafile ist noch nicht bereit, nach 20 bis 50 Sekunden erneut prüfen. |
rsync: command not found | Paket rsync installieren. |
Häufige Fragen
Wie viel RAM braucht der Stack wirklich?
Die Doku nennt 2 GB als Minimum. Gemessen wurden im Leerlauf mit 5 MB Daten rund 750 MiB; mit vielen Nutzern und Bibliotheken steigt der Bedarf.
Was tun bei vergessenem Admin-Passwort?
Die Doku beschreibt docker exec -it seafile /opt/seafile/seafile-server-latest/reset-admin.sh bei laufendem Container, das ein neues Admin-Konto anlegt.
Gibt Seafile gelöschten Speicher sofort frei?
Nein. Gelöschte Dateien belegen Blöcke, bis die Garbage Collection läuft; das Vorgehen steht in der Doku unter „Seafile GC“.
Redis oder Memcached?
Seit Seafile 13 ist Redis der Standard-Cache und im Stack enthalten; Memcached wird laut Doku nicht mehr mitgeliefert.
Fazit
Der offizielle Stack läuft auf einem Server mit 3 GB RAM ohne Anpassung und braucht im Leerlauf nur etwa ein Viertel davon. Stolpersteine sind das Protokoll in der .env und die nach dem ersten Start festen DB-Zugangsdaten. Mit Datenbank-Dump zuerst, dem Verzeichnis seafile und der .env lässt sich eine Instanz vollständig wiederherstellen.
Weiterführende Anleitungen und Quellen
- FileBrowser Quantum mit Docker Compose: schlankere Alternative, wenn nur ein Dateimanager im Browser gefragt ist.
- Backrest mit Docker Compose: verschlüsselte Offsite-Kopien des Backup-Verzeichnisses.
- Nextcloud nativ auf Ubuntu: Alternative mit Office- und Kalenderfunktionen.
- Seafile-Handbuch: Installation der Community Edition mit Docker
- Seafile-Handbuch: Backup und Wiederherstellung
- Seafile-Handbuch: Upgrade für Docker
- Seafile-Handbuch: Systemanforderungen
- Docker Hub: Tags von seafile-mc
- Repository haiwen/seafile
Eckdaten
| Image | seafileltd/seafile-mc:13.0-latest (13.0.28), mariadb:10.11, redis, seafileltd/sdoc-server:2.0-latest, lucaslorentz/caddy-docker-proxy:2.12-alpine |
|---|---|
| Ports | 80 und 443 (Caddy), Seafile intern 80 |
| Volumes | /opt/seafile-data, /opt/seafile-mysql/db, /opt/seafile-caddy, /opt/seadoc-data |
| Wichtige Variablen | SEAFILE_SERVER_HOSTNAME, SEAFILE_SERVER_PROTOCOL, JWT_PRIVATE_KEY, SEAFILE_MYSQL_DB_PASSWORD, INIT_SEAFILE_* |
| Healthcheck | curl -f http://localhost:80 im Container |
| Architekturen | amd64, arm64 |


