Seafile

Seafile ist eine Alternative zu Nextcloud und Owncloud. Die Lösung ist schlanker und schneller, nicht zuletzt deshalb, weil sie auf viele Features verzichtet und sich vornehmlich auf Synchronisation und Teilen von Dateien konzentriert. Die Community Edition ist Open Source. Hier beschreibe ich, wie ich sie unter Docker und unter Verwendung des Reverse Proxy Caddy bei einem Hoster betreibe.

Weboverfläche von Seafile
Abb. 1: Die Weboberfläche von Seafile


Installation der Software

Docker

Voraussetzung ist ein Linux-Server auf dem Docker installiert ist. Ich verwende dazu mein Bash-Script:


# Shell-Skript zur Installation von Docker (mit sudo ausführen:
#!/bin/bash
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do
apt-get remove $pkg;
done
apt-get update
apt-get install ca-certificates curl
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
echo \
 "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \
 $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update
apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
systemctl start docker
systemctl enable docker
usermod -aG docker "$SUDO_USER"
Hier ist das in der Version für Debian-Server. Für Ubuntu muss natürlich die Zeichenkette "debian" durch "ubuntu" ersetzt werden. Dieses Script, ich nenne es dockerinstall.sh, mache ich ausführbar und führe mit sudo, also mit Administrationsrechten aus.

chmod +x dockerinstall.sh
sudo ./dockerinstall.sh
Danach melde mich einmal kurz ab und wieder an, damit die neuen Berechtigungen greifen und ich ohne Root-Rechte docker ausführen kann.



Caddy

Seafile kann von Hause aus nur HTTP, also eine unverschlüsselte Verbindung. Das ist aber für Filesync und -sharing nicht vertretbar. Manche Clients lassen das auch gar nicht zu. Ich brauche also ein SSL-Zertifikat und muss den Zugang damit absichern. Darum kümmert sich Caddy. Caddy ist ein Reverseproxy. Er ermöglicht die Nutzung von HTTPS und kümmert sich auch selbständig um die Zertifikatsverwaltung via Let's Encrypt. Also muss ich nun zunächst Caddy installieren. Das geht unter Debian und Ubuntu einfach wie folgt:

# 1. Benötigte Pakete für die Key-Verwaltung installieren
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl

# 2. Den offiziellen Caddy-GPG-Key hinzufügen
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg

# 3. Die Caddy-Paketquelle hinzufügen
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list

# 4. Paketliste aktualisieren und Caddy installieren
sudo apt update
sudo apt install caddy


Installation von Seafile mittels Docker Compose

Zunächst erstelle ich einen Ordner für die Compose-Files. Ich mache das üblicherweise im Homeshare also muss ich einfach mit


mkdir seafile
cd seafile
einen Ordner erstellen und öffnen. Dann erstelle ich zunächst das File mit den Umgebungsvariablen mit

nano .env
Der Punkt am Anfang des Namens ist wichtig, damit es eine versteckte Datei ist und damit Docker das File auch findet. Es bekommt folgenden Inhalt:

# Datenbank (Generiere hier ein neues, langes Passwort!)
DB_ROOT_PASSWORD=EinmöglichstkomplexesundgeheimesPasswortwiezumBeispiel123456
DB_PASSWORD=dto

# Seafile Admin
SEAFILE_ADMIN_EMAIL=mail@adresse.tld
SEAFILE_ADMIN_PASSWORD=nocheingeheimeslpasswort

# Wichtig: Die echte Domain ohne Protokoll
SEAFILE_SERVER_HOSTNAME=(sub)domain.des.servers

# Zeitzone
TIME_ZONE=Europe/Berlin

# JWT Schlüssel (Neu generieren: openssl rand -base64 32)
JWT_PRIVATE_KEY=Mit openssl rand -base64 32 generieren und hier einfügen!
Dann brauche ich noch eine Datei docker-compose.yml mit folgendem Inhalt:

services:
  db:
    image: mariadb:10.11
    container_name: seafile-mysql
    restart: unless-stopped
    environment:
      - MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
      - MARIADB_AUTO_UPGRADE=1
    volumes:
      - /opt/seafile/shared/db:/var/lib/mysql
    networks:
      - seafile-internal

  memcached:
    image: memcached:1.6
    container_name: seafile-memcached
    entrypoint: memcached -m 256
    networks:
      - seafile-internal

  seafile:
    image: seafileltd/seafile-mc:latest
    container_name: seafile
    restart: unless-stopped
    ports:
      - "127.0.0.1:8080:80"   # Web-UI (nur lokal für Caddy)
      - "127.0.0.1:8082:8082" # Fileserver (nur lokal für Caddy)
    environment:
      - DB_HOST=db
      - DB_ROOT_PASSWD=${DB_ROOT_PASSWORD}
      - TIME_ZONE=${TIME_ZONE}
      - SEAFILE_ADMIN_EMAIL=${SEAFILE_ADMIN_EMAIL}
      - SEAFILE_ADMIN_PASSWORD=${SEAFILE_ADMIN_PASSWORD}
      - SEAFILE_SERVER_HOSTNAME=${SEAFILE_SERVER_HOSTNAME}
      - SEAFILE_SERVER_LETSENCRYPT=false # Caddy macht SSL, nicht Seafile!
      - JWT_PRIVATE_KEY=${JWT_PRIVATE_KEY}
    volumes:
      - /opt/seafile/shared/seafile-data:/shared
    depends_on:
      - db
      - memcached
    networks:
      - seafile-internal

networks:
  seafile-internal:
    driver: bridge

Hier braucht nichts angepasst zu werden, weil die Parameter in der .env-Datei stehen.



Konfigurationen

Caddy-Konfiguration

Für Caddy muss ich noch ein Caddyfile schreiben. Ich erstelle (oder falls vorhanden bearbeite) eine Datei namens /etc/caddy/Caddyfile mit folgendem Inhalt:


# Die Domain beim Hoster
sub.domain.tld {
    # Logging (optional, aber nützlich)
    log {
        output file /var/log/caddy/seafile_access.log
    }

    # Der eigentliche Proxy zur Seafile Web-UI
    reverse_proxy 127.0.0.1:8080

    # Spezielle Behandlung für den Fileserver (Upload/Download)
    handle_path /seafhttp* {
        reverse_proxy 127.0.0.1:8082
    }

    # Sicherheit: HSTS aktivieren
    header {
        Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
    }
}
Das ist schon alles. Nun starte ich Caddy einmal neu mit

sudo systemctl restart caddy
und der Reverseproxy ist am Start.



Konfiguration Seafile

Leider können nicht alle für den Betrieb erforderlichen Parameter über das .env-File oder die Compose-Datei definiert werden. Ich muss auch noch eine Datei mit Einstellungen bearbeiten, die aber erst beim ersten Starten des Seafile-Containers erzeugt wird. Ich starte daher die Container mit


docker compose up -d
Dabei ist natürlich wichtig, dass ich mich auf der Shell in dem Ordner mit der Datei docker-compose.yml befinde, in meinem Beispiel also im Verzeichnis saefile in meinem Homeshare auf dem Server. Mit der Volume-Definition "- /opt/seafile/shared/seafile-data:/shared" in der Compose-Datei habe ich vorgegeben, dass die Systemdateien im Verzeichnis /opt/seafile erstellt werden. Im Pfad /opt/seafile/shared/seafile-data/seafile/conf befindet sich nun eine Datei namens seahub_settings.py die ich anpassen muss.

sudo nano /opt/seafile/shared/seafile-data/seafile/conf/seahub_settings.py
	
Sie bekommt folgenden Inhalt bzw. wird wie folgt ergänzt:


# -*- coding: utf-8 -*-
SECRET_KEY = "wurde automatisch erzeugt; nicht ändern!"
SERVICE_URL = "https://sub.domain.tld"

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'seahub_db',
        'USER': 'seafile',
        'PASSWORD': 'nichtändern',
        'HOST': 'db',
        'PORT': '3306',
        'OPTIONS': {'charset': 'utf8mb4'},
    }
}


CACHES = {
    'default': {
        'BACKEND': 'django_pylibmc.memcached.PyLibMCCache',
        'LOCATION': 'memcached:11211',
    },
    'locmem': {
        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
    },
}
COMPRESS_CACHE_BACKEND = 'locmem'
TIME_ZONE = 'Europe/Berlin'
FILE_SERVER_ROOT = "https://sub.domain.tld/seafhttp"
CSRF_TRUSTED_ORIGINS = ["https://sub.domain.tld"]


# E-Mail Einstellungen
EMAIL_USE_TLS = False
EMAIL_HOST = "mail.domain.tld"                  # SMTP-Server deines Providers
EMAIL_PORT = 465                                # Port für TLS (meist 587)
EMAIL_HOST_USER = "seafile@domain.dlt"          # Dein Benutzername/E-Mail
EMAIL_HOST_PASSWORD = "TrIuMp,66,="             # Dein Passwort
EMAIL_FROM_USER = "seafile@domain.tld"          # Absender-Adresse
SERVER_EMAIL = "seafile@domain.tld"             # Adresse für System-Benachrichtigungen
DEFAULT_FROM_EMAIL = EMAIL_FROM_USER
EMAIL_USE_SSL = True 

	
Danach kann man sich (hoffentlich) zum ersten Mal auf der Oberfläche mit dem zuvor definierten Account (s.o. SEAFILE_ADMIN_EMAIL und SEAFILE_ADMIN_PASSWORD) anmelden.



Backup

Ohne Datensicherung ist ein Cloudspeicher natürlich nicht sicher zu betreiben. Für Seafile müssen die Datenbankinhalte und die Daten innerhalb des Verzeichnisses /opt/seafile gesichert werden. Die eigentliche Sicherung mache ich mit duplicati aber zunächst brauche ich einen Datenbankdump, damit die Wiederherstellung im Notfall funktioniert. Dazu erstelle ich ein kleines Script im Verzeichnis /opt/ namens seafile-backup.sh. Dieses Script habe ich freundlicherweise von Gemini erstellt bekommen. Ich habe es getestet und für gut befunden.


sudo nano /opt/seafile-backup.sh
	
mit folgendem Inhalt:

#!/bin/bash

# --- KONFIGURATION ---
BACKUP_DIR="/opt/seafile/shared/backups" # Verzeichnis auf dem HOST
CONTAINER_NAME="seafile-mysql"
DB_USER="root"
DB_PASS="DEIN_PASSWORT_OHNE_LEERZEICHEN"
KEEP_DAYS=7 # Wie viele Tage sollen die lokalen SQL-Dumps behalten werden?

# Datum für den Dateinamen
DATE=$(date +%Y-%m-%d_%H-%M)
FILENAME="seafile_complete_$DATE.sql"

# Verzeichnis erstellen, falls nicht vorhanden
mkdir -p $BACKUP_DIR

# --- DUMP ERSTELLEN ---
echo "Starte Datenbank-Backup für Seafile..."

docker exec $CONTAINER_NAME mariadb-dump \
    -u $DB_USER \
    -p$DB_PASS \
    --databases ccnet_db seafile_db seahub_db > $BACKUP_DIR/$FILENAME

# Prüfen, ob der Dump erfolgreich war
if [ $? -eq 0 ]; then
    echo "Backup erfolgreich: $FILENAME"
    # Optional: Erfolgsmeldung per Mail (falls 'mailutils' installiert sind)
    # echo "Seafile Backup erfolgreich erstellt am $DATE" | mail -s "Seafile Backup OK" mail@deine-domain.de
else
    echo "FEHLER: Backup fehlgeschlagen!" >&2
    exit 1
fi

# --- ALTE DUMPS LÖSCHEN ---
# Löscht Dateien, die älter als KEEP_DAYS sind, um Platz zu sparen
find $BACKUP_DIR -name "seafile_complete_*.sql" -mtime +$KEEP_DAYS -exec rm {} \;

echo "Bereinigung abgeschlossen."
	
Dieses Script mache ich natürlich ausführbar mit

sudo chmod +x /opt/seafile-backup.sh
	
und teste es mit

sudo /opt/seafile-backup.sh
	
Wenn dabei kein Fehler auftritt, dann meldet das Script "Backup erfolgreich: seafile_complete_JJJ-MM-TT_hh-mm.sql". Ich kann das Script nun also in die Crontab eintragen, damit es nächtens automatisert ausgeführt wird.

Ich rufe die crontab-Bearbeitung für den Administrationsuser mit

sudo crontab -e
            
auf. Wenn ich bisher keinen Standard-Editor für die Bearbeitung der Crontab angegeben habe, werde ich nun danach gefragt. Hier wähle ich mit Enter den vorgeschlagenen Editor nano aus:

no crontab for andreas - using an empty one

Select an editor.  To change later, run 'select-editor'.
  1. /bin/nano        <---- easiest
  2. /usr/bin/vim.basic
  3. /usr/bin/vim.tiny
  4. /bin/ed

Choose 1-4 [1]: 1
            
An das Ende dieser Datei trage ich dann ein:

0 7 * * * /opt/seafile-backup.sh
            
So werden die Backups immer morgens um 7:00 Uhr durchgeführt und die Benachrichtigungen zu Erfolg oder Misserfolg der Aktion werden an den User root gemailt (wenn Mails auf dem System konfiguriert sind). Dann richte ich die eigentliche Datensicherung mit duplicati ein. Damit sichere ich einfach den kompletten Ordner /opt/seafile



Exkurs: Sicherung einzelner Dateien/Dokumente

Seafile speichert die Dokumente in Chunks aufgesplittet. Dadurch ist ein Restore nicht für einzelne Dokumente, sondern nur komplett möglich. Wenn man nun aber einzelne Dokumente sichern und wiederherstellen möchte, dann kann man sie zu diesem Zweck zunächst temporär mounten. Dazu gibt es im Container seafile das script seaf-fuse.sh. Das folgende Script - ich nenne es backupfiles.sh - mountet zunächst temporär die Bibliotheken in ein Verzeichnis auf dem Server und synchronisiert die Dokumente mit einem lokalen Verzeichnis. Dabei werden verschlüsselte Verzeichnisse ausgelassen. Außerdem wird so der doppelte Speicher belegt und die Dateien liegen offen auf dem Server. Die Chunks stellen aber natürlich auch keine Verschlüsselung dar.


#!/bin/bash

# 1. Sicherstellen, dass der Mount-Punkt leer/sauber ist
fusermount -u /opt/seafile-fuse 2>/dev/null

# 2. Mount im Container starten
docker exec seafile /opt/seafile/seafile-server-latest/seaf-fuse.sh start /shared/fuse

# 3. Kurz warten, bis das Dateisystem bereit ist
sleep 5

# 4. Der Backup-Befehl (hier rsync)
# Hier werden jetzt alle Dateien im Klartext kopiert
rsync -av --delete /opt/seafile-fuse/ /opt/seafile/filebackup

# 5. Sauber unmounten
docker exec seafile /opt/seafile/seafile-server-latest/seaf-fuse.sh stop

sudo umount -l /opt/seafile-fuse 2>/dev/null

            
Das Script wird natürlich ausführbar gemacht

chmod +x backupfiles.sh
            
und kann dann mit Administrationsrechten ausgeführt werden. Testweise kann man es aufrufen mit

sudo ./backupfiles.sh
            
Wenn das soweit klappt, kann man das Script mittels Cronjob automatisieren (s.o).



Nutzung von Seafile

Die Weboberfläche von Seafile ist recht selbsterklärend und wird hier nicht weiter erläutert. Für Android, iOS, MacOS, Windows und Linux gibt es Clients. Nähere Informationen dazu siehe: Downloadseite von Seafile und natürlich die App-Shops von Google und Apple.