Files
oikos/containers/125-seafile.md
Claudio on hubris 40f6c93fd5 Add 125 (seafile) — Nextcloud-replacement evaluation
LXC 125 stood up as a Seafile CE 13.0 docker-compose deployment, behind
files.hubris.network. Authentik OAuth wired up via ak shell. No data
migration — exploration alongside Nextcloud (114).
2026-05-12 12:45:45 +02:00

6.1 KiB

125 — seafile

Seafile Community Edition 13.0 — exploratory deployment to evaluate as a possible nextcloud (114) replacement. Stood up empty on 2026-05-12; no migration, NC stays untouched.

At a glance

  • Hostname: seafile
  • IP: 192.168.8.185
  • Privilege: privileged (Debian 12, nesting + keyctl)
  • Resources: 4 cores / 8 GiB RAM / 32 GiB rootfs / 1 GiB swap
  • Mounts: /mnt/library/mnt/library (Seafile storage under /mnt/library/seafile/data)
  • Public hostname: files.hubris.networkcaddy (121)192.168.8.185:80
  • Reachable from: LAN + Netbird mesh (no public ingress)

Stack

Docker-compose at /opt/seafile/, three services on a private seafile-net bridge:

Service Image Role
seafile seafileltd/seafile-mc:13.0-latest Seahub + seaf-server + Apache (the -mc flavor; uses Redis for cache)
db mariadb:10.11 ccnet / seafile / seahub databases
redis redis:7-alpine session + cache backend

Compose layout:

File Purpose
/opt/seafile/seafile-server.yml upstream-verbatim from manual.seafile.com/13.0/repo/docker/ce/
/opt/seafile/docker-compose.override.yml local: publishes 192.168.8.185:80, extra_hosts: auth.hubris.network:192.168.8.175, drops bundled-caddy labels (labels: !reset [])
/opt/seafile/.env mode 600, not git-tracked — DB passwords, JWT_PRIVATE_KEY, INIT_SEAFILE_ADMIN_*, SEAFILE_SERVER_HOSTNAME=files.hubris.network, SEAFILE_SERVER_PROTOCOL=https, ENABLE_SEADOC=false

Do not rotate JWT_PRIVATE_KEY — it invalidates every active session and library access token.

Storage layout

  • /mnt/library/seafile/data/ — bind-mounted to /shared in the seafile container. Holds config (seafile/conf/), logs (seafile/logs/), and the heavy block/fs/commit trees (seafile/seafile-data/storage/). Group-owned :media 2775.
  • /opt/seafile/mysql/ — mariadb data (small, stays on rootfs).

Auth

Native OAuth2/OIDC against authentik (124). Configuration in /mnt/library/seafile/data/seafile/conf/seahub_settings.py between the # BEGIN-HUBRIS-OAUTH / # END-HUBRIS-OAUTH markers.

Authentik side:

  • Provider: Provider for Seafile (OAuth2/OIDC, confidential, signing key = self-signed cert)
  • Application slug: seafile
  • Redirect URI: https://files.hubris.network/oauth/callback/ (trailing slash mandatory)
  • Launch URL: https://files.hubris.network/
  • Policy binding: authentik Admins group
  • Provisioned via ak shell script (idempotent — OAuth2Provider.objects.get_or_create(name="Provider for Seafile"))

Seahub side:

  • ENABLE_OAUTH = True
  • OAUTH_AUTHORIZATION_URL / OAUTH_TOKEN_URL / OAUTH_USER_INFO_URLhttps://auth.hubris.network/application/o/{authorize,token,userinfo}/
  • OAUTH_SCOPE = ['openid', 'profile', 'email']
  • OAUTH_ATTRIBUTE_MAP = {'email': (True, 'contact_email'), 'name': (False, 'name'), 'id': (False, 'not_used')}
  • OAUTH_CREATE_UNKNOWN_USER = True, OAUTH_ACTIVATE_USER_AFTER_CREATION = True — JIT provisioning enabled.

The Seahub login page renders a "Single Sign-On" button that 302s through /sso//oauth/login/ → Authentik flow.

Reverse-proxy settings (also in seahub_settings.py)

SERVICE_URL = "https://files.hubris.network"
FILE_SERVER_ROOT = "https://files.hubris.network/seafhttp"
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
CSRF_TRUSTED_ORIGINS = ['https://files.hubris.network']

DNS / mesh

  • LAN clients resolve files.hubris.network192.168.8.175 via dnsmasq on LXC 124 (entry: address=/files.hubris.network/192.168.8.175).
  • Netbird mesh peers reach 192.168.8.185 through the existing 192.168.8.0/24 route advertised by the hubris peer.
  • The Seafile container itself needs auth.hubris.network → 192.168.8.175 mapped at the Docker level (Docker's resolver doesn't follow LXC /etc/hosts or the dnsmasq split-horizon), so docker-compose.override.yml carries extra_hosts: auth.hubris.network:192.168.8.175. See mesh migration for the pattern.

Caddy block (LXC 121)

files.hubris.network {
    tls {
        dns ionos {env.IONOS_AUTH_API_TOKEN}
    }
    encode zstd gzip
    request_body {
        max_size 10GB
    }
    reverse_proxy 192.168.8.185:80 {
        header_up X-Forwarded-Proto https
        header_up X-Forwarded-Host {host}
        header_up Host {host}
    }
}

No import authentik — Seafile handles OAuth natively, forward-auth would block the /oauth/callback/ round-trip.

Why exploration-only (and not a Nextcloud cutover)

The mulita (120) photo stack is tightly coupled to Nextcloud — it reads storage roots from /mnt/library/homecloud/{user}/files/, proxies thumbnails through NC's /core/preview, and consumes NC webhook_listeners for file events. Migrating off Nextcloud would require re-architecting all three integration points first.

For now: stand up Seafile, kick the tires, decide later whether to:

  1. Keep NC for photos and move docs/files to Seafile.
  2. Refactor mulita off NC, then full cutover.
  3. Roll back (Seafile is rollback-safe — NC is untouched throughout).

Changelog

2026-05-12 — initial deployment

LXC 125 created (privileged Debian 12, 4 c / 8 GiB / 32 GiB, IP 192.168.8.185). Seafile CE 13.0 docker-compose stack at /opt/seafile/, storage on /mnt/library/seafile/data. Authentik OAuth provider + Application provisioned via ak shell (slug seafile, redirect URI https://files.hubris.network/oauth/callback/). Caddy site block added on LXC 121, dnsmasq entry on LXC 124. End-to-end OAuth handshake verified to the Authentik flow page. No data migration — exploratory deployment alongside Nextcloud.