Seafile Pro
Seafile Pro is private cloud storage with browser-based Office editing (OnlyOffice), collaborative document editing (SeaDoc), full-text search (SeaSearch) and antivirus scanning (ClamAV). Its services are split over Compose files that COMPOSE_FILE combines — the example enables seven — and it needs a commercial license. OnlyOffice is a separate server and is enabled only when ONLYOFFICE_JWT_SECRET is set; the example ships it empty.
Last run on a host 8 Nov 2026 · data restored from a backup
Pinned at 13.0.29 (released 4 Nov 2026) · upstream has released 14.0.9 on 13 Oct 2026
In the upstream images: 23 CRITICAL · 783 HIGH with a published fix — per image
To try it: Docker, and a Seafile Pro commercial licence, which the image requires either way. See Try it locally.
To deploy it behind the proxy: Traefik running, a hostname, a Seafile Pro commercial license, and an external OnlyOffice instance for .docx/.xlsx/.pptx editing (optional — all other features work without it).
Try it locally
Section titled “Try it locally”On your own machine, with no proxy, domain or certificate. From apps/seafile-pro/:
cp .env.local.example .env.local
# The secrets ship empty; generate the ones this stack needs{ echo "SEAFILE_MYSQL_DB_PASSWORD=$(openssl rand -base64 32 | tr -d '\n')" echo "INIT_SEAFILE_MYSQL_ROOT_PASSWORD=$(openssl rand -base64 32 | tr -d '\n')" echo "REDIS_PASSWORD=$(openssl rand -hex 32)" echo "INIT_SEAFILE_ADMIN_PASSWORD=$(openssl rand -base64 32 | tr -d '\n')" echo "JWT_PRIVATE_KEY=$(openssl rand -base64 48 | tr -d '\n')"} >> .env.local
mkdir -p volumes/local/mysql volumes/local/redis volumes/local/seafile-datasudo chown 999:999 volumes/local/mysql
docker compose -f docker-compose.local.yml --env-file .env.local up -dThen open http://127.0.0.1:8001. Stop it again with the same command and down.
Installation
Section titled “Installation”From apps/seafile-pro/:
-
Terminal window cp .env.example .env -
Set required values in
.env:APP_TRAEFIK_HOST=files.example.comSEAFILE_ADMIN_EMAIL[email protected]TZ=Europe/Vienna -
Generate passwords and the JWT key:
Terminal window sed -i "s|^INIT_SEAFILE_MYSQL_ROOT_PASSWORD=.*|INIT_SEAFILE_MYSQL_ROOT_PASSWORD=$(openssl rand -base64 32 | tr -d '\n')|" .envsed -i "s|^SEAFILE_MYSQL_DB_PASSWORD=.*|SEAFILE_MYSQL_DB_PASSWORD=$(openssl rand -base64 32 | tr -d '\n')|" .envsed -i "s|^INIT_SEAFILE_ADMIN_PASSWORD=.*|INIT_SEAFILE_ADMIN_PASSWORD=$(openssl rand -base64 32 | tr -d '\n')|" .envsed -i "s|^JWT_PRIVATE_KEY=.*|JWT_PRIVATE_KEY=$(openssl rand -base64 48 | tr -d '\n')|" .envsed -i "s|^REDIS_PASSWORD=.*|REDIS_PASSWORD=$(openssl rand -hex 32)|" .env -
If using OnlyOffice, set the hostname and copy the JWT secret — it must match the OnlyOffice server exactly:
ONLYOFFICE_HOST=office.example.comTerminal window # sed -i "s|^ONLYOFFICE_JWT_SECRET=.*|ONLYOFFICE_JWT_SECRET=<value-from-apps/onlyoffice>|" .env -
Pull images:
Terminal window docker compose pull -
Start the stack:
Terminal window docker compose up -d -
Wait for all containers to be healthy — first boot initialises the databases and takes roughly three minutes:
Terminal window docker compose ps -
Required: restart the app container once to inject Blueprint configs into Seafile’s settings files. This step only needs to run once — a marker prevents it from repeating on subsequent restarts:
Terminal window docker compose restart seafile-pro-app -
Build the initial full-text search index:
Terminal window docker exec seafile-pro-app /opt/seafile/seafile-server-latest/pro/pro.py search --update
Verify
Section titled “Verify”docker compose ps # every service of the files in COMPOSE_FILE up; app, db, redis, clamav should show healthyConfirm the config injection from step 8 ran:
docker exec seafile-pro-app grep "Blueprint" /shared/seafile/conf/seahub_settings.pydocker exec seafile-pro-app grep "SEASEARCH" /shared/seafile/conf/seafevents.confdocker exec seafile-pro-app grep "virus_scan" /shared/seafile/conf/seafile.confEach command should return a matching line. No output means the injection step was skipped — run docker compose restart seafile-pro-app and recheck.
Confirm the notification server is reachable:
curl -s https://your-domain/notification/ping # {"ret": "pong"}Test antivirus:
docker exec seafile-pro-app bash -c "curl -s https://secure.eicar.org/eicar.com.txt | clamdscan -"# Expected: stream: Eicar-Test-Signature FOUNDThen in the browser, confirm each service works:
- Log in with the admin account.
- Upload a file and download it back.
- Create and edit a Markdown (
.md) file. - Open a
.docxin the OnlyOffice editor (requires OnlyOffice configured). - Create a
.sdocfile and open it in SeaDoc. - Search for a word from an uploaded file — SeaSearch should return it.
OnlyOffice
Section titled “OnlyOffice”OnlyOffice embeds a browser-based Office editor into Seafile. It runs as a separate server — either another Blueprint stack or a different machine — and both sides share the same JWT secret.
Server-to-server path: OnlyOffice fetches the document from Seafile and saves it back through a callback. Seafile’s route ships closed, and a closed route refuses every container, so on the same host these two paths have their own opt-in routes in onlyoffice-callback.yml (the same mechanism as Seafile CE); an OnlyOffice server on another host reaches a closed Seafile only by its address in TRUSTED_RANGES. DOCX, XLSX and PPTX open, save and reopen through onlyoffice-callback.yml.
Allowed origins: On the OnlyOffice server, add the Seafile origin to ONLYOFFICE_ALLOWED_ORIGINS in its .env so browsers are permitted to embed the editor in an iframe. In the Blueprint’s apps/onlyoffice stack this value is embedded in a Traefik label — changing it takes effect only after recreating the container, not after a plain restart:
# On the OnlyOffice server:docker compose up -d --force-recreateGoing further
Section titled “Going further”SMTP is optional but required for password reset emails, share invitations, and file change notifications. Set the SMTP variables in .env:
SEAFILE_SMTP_HOST=smtp.example.comSEAFILE_SMTP_PORT=587SEAFILE_SMTP_USER[email protected]SEAFILE_SMTP_FROM[email protected]SEAFILE_SMTP_USE_TLS=trueSEAFILE_SMTP_PASSWORD=your-smtp-passwordThen recreate the app so it picks up the new values — docker compose restart keeps the environment the container was created with, so a changed
.env would have no effect:
docker compose up -d seafile-pro-appSend a test email from the Seafile admin panel before relying on this for password resets.
The stack ships APP_TRAEFIK_ACCESS=access-trusted-vpn — the ranges declared in core/traefik/.env and the VPN; no closed policy admits a Docker container; SeaDoc calls Seafile over the internal network, and the two paths OnlyOffice uses — the document fetch and the save callback — have their own routes in onlyoffice-callback.yml. access-public removes Traefik’s source-IP allowlist. Whether the service is actually reachable from the public internet depends on your DNS records, routing, and upstream firewall — the access-public middleware setting does not control any of that.
Setting APP_TRAEFIK_ACCESS=access-vpn admits VPN clients only; the declared ranges no longer.
With the shipped published ports Traefik sees a client’s own address on IPv4 and IPv6, from a tailnet client as from one off the host. If it sees a Docker gateway address instead, see Troubleshooting; widening the policy would admit every caller that arrives through that gateway.
ClamAV is included in the default COMPOSE_FILE and configured automatically when the Blueprint config injection runs (step 8 of the installation). Uploaded files are scanned on write.
To verify ClamAV is active:
docker exec seafile-pro-app bash -c "curl -s https://secure.eicar.org/eicar.com.txt | clamdscan -"# Expected: stream: Eicar-Test-Signature FOUNDA startup log warning about the clamd socket (Clamd was NOT notified) is expected during container initialisation — it is a timing race and does not affect scanning once the container is up.
SeaSearch is the default full-text search backend. It is lightweight and configured automatically by the config injection step — no manual setup required.
Elasticsearch is available in the repository as a reference compose file (elasticsearch.yml) but is not active by default and not included in COMPOSE_FILE. Do not add it alongside SeaSearch — both cannot run simultaneously.
Trigger a manual index rebuild after bulk uploads or a first install:
docker exec seafile-pro-app /opt/seafile/seafile-server-latest/pro/pro.py search --updateBackup
Section titled “Backup”Backup and restore covers the host side — Borgmatic, the repository, the schedule and the database hooks. What this stack contributes:
volumes/seafile-data/— all user files and library datavolumes/mysql/— all three databases (Seafile, Seahub, Ccnet)volumes/seadoc-data/— collaborative document state- The
.envfile and any secrets
The search index is not on that list. It is rebuilt rather than restored, with the command under Going further.
The databases and the file volumes have to come back from the same point in time, and all three databases together. A restore that recovers one side without the other leaves the installation inconsistent rather than merely incomplete.
The database password lives in .env, and borgmatic reads credentials from a
file, so write one for it first:
mkdir -p .secretsgrep '^SEAFILE_MYSQL_DB_PASSWORD=' .env | cut -d= -f2- | tr -d '\n' > .secrets/seafile_db_pwd.txtchmod 600 .secrets/seafile_db_pwd.txtFor borgmatic:
mariadb_databases: - name: ccnet_db container: seafile-pro-db username: seafile password: "{credential file /srv/docker/apps/seafile-pro/.secrets/seafile_db_pwd.txt}" - name: seafile_db container: seafile-pro-db username: seafile password: "{credential file /srv/docker/apps/seafile-pro/.secrets/seafile_db_pwd.txt}" - name: seahub_db container: seafile-pro-db username: seafile password: "{credential file /srv/docker/apps/seafile-pro/.secrets/seafile_db_pwd.txt}"Restore order: databases, then the block store, then the application, then the
search reindex. A dump holds the databases, not the account Seafile connects with
— restored into a fresh db container, every request fails with Access denied for user 'seafile'. Create the account before starting the app:
docker compose up -d db# restore the three dumps, then:docker compose exec -T db sh -c 'mariadb -uroot -p"$MYSQL_ROOT_PASSWORD"' <<SQLCREATE USER IF NOT EXISTS 'seafile'@'%' IDENTIFIED BY '$(grep '^SEAFILE_MYSQL_DB_PASSWORD=' .env | cut -d= -f2- | tr -d '\n')';GRANT ALL PRIVILEGES ON ccnet_db.* TO 'seafile'@'%';GRANT ALL PRIVILEGES ON seafile_db.* TO 'seafile'@'%';GRANT ALL PRIVILEGES ON seahub_db.* TO 'seafile'@'%';SQLdocker compose up -dUpdates
Section titled “Updates”This sequence follows the upstream Docker upgrade guide.
-
Read the Seafile Pro release notes and the Docker upgrade guide for breaking changes.
-
Back up all volumes before touching the stack.
-
Update image tags in
.env—APP_TAGfor the main server, and the tags for any component services that changed. -
Pull and recreate:
Terminal window docker compose pulldocker compose up -d -
Watch app logs for any database migration output:
Terminal window docker compose logs -f seafile-pro-app -
Verify login, file access, editing, and search after the update.
Troubleshooting
Section titled “Troubleshooting”OnlyOffice editor not loading: either a JWT mismatch — ONLYOFFICE_JWT_SECRET in apps/seafile-pro/.env has to match the secret on the OnlyOffice server exactly — or the Seafile origin is missing from ONLYOFFICE_ALLOWED_ORIGINS on the OnlyOffice side.
Allowed-origins change has no effect: ONLYOFFICE_ALLOWED_ORIGINS is embedded in a Traefik label at container creation time, so a plain docker compose restart does not pick up a new value. Run docker compose up -d --force-recreate on the OnlyOffice server.
ClamAV socket warning in startup logs: the line Clamd was NOT notified: Can't connect to clamd through /tmp/clamd.sock is a timing race during container initialisation and is non-fatal. Run the EICAR test in Verify to confirm scanning is active.
MariaDB io_uring warning or Redis memory overcommit warning: both are host-level configuration notices, not application errors. To silence the Redis warning, set vm.overcommit_memory = 1 in /etc/sysctl.conf on the host and apply it with sysctl -p. The MariaDB notice needs no action — it falls back to libaio on its own.
access-vpn returning 403 for a Tailscale client: see Traefik troubleshooting for the source-IP caveat when traffic arrives through Docker published ports.
Repository files
Section titled “Repository files”apps/seafile-pro/README.md— full setup, config injection details, and verification commandsapps/seafile-pro/UPSTREAM.md— upstream notes, known limitations, troubleshooting table, and upgrade checklistapps/seafile-pro/.env.example— all configurable variables with inline comments
Official sources
Section titled “Official sources”© 2026 SecDockBlueCode Apache-2.0Site content CC BY-NC 4.0