Fully verified15,195

Cloud storage

Seafile

Seafile syncs files as deduplicated blocks rather than as a mirrored directory, which is what makes large collections and interrupted transfers fast. Each library syncs separately to desktop, mobile, and the browser, can be shared by link or account, and can be encrypted client-side so the server never sees its contents.

Overview

Seafile is file sync and share built around libraries: each library is a separate versioned repository that syncs to a desktop client, a phone, or a web browser, and can be shared by link or by account. Files are stored as deduplicated blocks rather than as a mirrored directory tree, which is what makes syncing large collections and resuming interrupted transfers fast. A library can be encrypted client-side, so the server never sees its contents.

This recipe runs pinned Seafile Community Edition 13.0.25 with MariaDB 10.11 and a Valkey cache. The server keeps its configuration, logs, and the whole object store in one volume; the three databases hold accounts, libraries, and metadata. The web port binds to localhost, the SeaDoc editor, the notification server, and the AI module are switched off because they are separate containers this recipe does not ship.

Security and recipe boundaries

The full application smoke test and practical backup and restore have passed on amd64. The published image supports amd64 only — the ARM builds upstream offers are marked testing — so the manifest declares one architecture rather than promising a Raspberry Pi. The administrator account, the MariaDB root password, the database password, the cache password, and the JWT key all come from .env: give it mode 600, keep it out of Git, and encrypt every archive, because the same file also decrypts the backup. SEAFILE_SERVER_HOSTNAME and SEAFILE_SERVER_PROTOCOL are baked into links, invitation e-mail, and client configuration at first start; changing the domain later means editing the generated configuration inside the volume. SAML and the advanced administration features belong to the Professional Edition and are not part of this recipe.

Update tracking

These are stored results of separate checks, not real-time data. An unknown status means the check has not been performed yet.

New releases

Recipe version compared with the latest known upstream release. A version number, not a reading of what changed.

Current version 13.0.25Sep 2, 2026, 8:10 PM UTC

Critical vulnerabilities

Result of a dedicated Docker image scan, not a Compose configuration check.

Not checkedNot checked

Breaking updates

A new major version is found by comparing version numbers. A compatibility verdict is entered by hand after reading the release notes and the migration steps.

Not checkedNot checked

compose.yaml

Upstream Compose changes relative to the verified recipe.

Upstream Compose is not trackedNot checked

Outdated Docker images

Pinned image tags checked for newer versions.

Not checkedNot checked

From observation to action

Update plan

The recipe already pins the latest known release. The steps below show the evidence retained for that version.

13.0.2513.0.25
  1. Open the release evidence

    There is no exact release-notes URL for this manually tracked source. Start with the project sources below.

  2. Review the version boundary

    The version numbers do not cross a major compatibility boundary. No compatibility verdict has been recorded.

  3. Prepare image-tag changes

    No image-tag change is currently planned.

  4. Follow the documented migration shape

    Migration, majors cannot be skipped. Release-specific migration steps still come from the release notes and installation guide.

  5. Protect the rollback point

    No version change is currently planned. The recipe includes a backup procedure. Restore has been tested.

  6. Verify the updated recipe

    An exact Fully verified check for this target was recorded on 2026-09-03.

Read the update procedure in the installation guide ↓

Specifications

Security and exposure

Installation levels

The status shows how fully the published recipe meets each level's requirements.

Partial

For home

A minimal setup for a local network or access through a VPN.

  • Single server
  • Local access
  • Minimum components

You still need to add: single server, local access, minimum components.

Partial

For a VPS

A public deployment with protected access and backups.

  • Domain
  • HTTPS via reverse proxy
  • Off-server backup

You still need to add: domain, https via reverse proxy, off-server backup.

Partial

Reliable

A setup designed for predictable operations and observability.

  • Separate database
  • Healthcheck
  • External monitoring
  • CPU and memory limits

You still need to add: external monitoring, cpu and memory limits.

Tailored to your server

Configuration generator

Answer five questions and download a ready-to-run Seafile bundle.

Runs locally
01 Do you have a domain?
02 Which reverse proxy?
03 Server architecture
04 Where should data be stored?

Use an absolute path for a separate disk, such as /srv/seafile.

05 Is VPN access required?

Your bundle is ready

The archive is created in your browser. Your answers are never sent anywhere.

Screenshots

Screenshots have not been added yet.

Quick start

Read the full guide first: it explains external access, backups, and data locations.

Prepare settingscp .env.example .env
Launchdocker compose up -d
Check statusdocker compose ps

Step-by-step guide

1. Check the Ubuntu or Debian server

Use Ubuntu 22.04+ or Debian 12+ with Docker Engine and Compose v2.24+. Upstream asks for at least 2 CPU cores and 2 GB RAM; plan 4 GB and disk for the whole library collection plus the file history each library keeps. The published image is amd64 only, so an ARM board is not a supported target for this recipe.

docker --version
docker compose version

2. Prepare the recipe and four independent secrets

mkdir -p ~/services/seafile
cd ~/services/seafile
cp .env.example .env
chmod 600 .env
admin_password="$(openssl rand -hex 24)"
db_root_password="$(openssl rand -hex 32)"
db_password="$(openssl rand -hex 32)"
jwt_key="$(openssl rand -hex 32)"
cache_password="$(openssl rand -hex 32)"
sed -i "s|^SEAFILE_ADMIN_PASSWORD=.*|SEAFILE_ADMIN_PASSWORD=$admin_password|" .env
sed -i "s|^SEAFILE_DB_ROOT_PASSWORD=.*|SEAFILE_DB_ROOT_PASSWORD=$db_root_password|" .env
sed -i "s|^SEAFILE_DB_PASSWORD=.*|SEAFILE_DB_PASSWORD=$db_password|" .env
sed -i "s|^SEAFILE_JWT_PRIVATE_KEY=.*|SEAFILE_JWT_PRIVATE_KEY=$jwt_key|" .env
sed -i "s|^SEAFILE_CACHE_PASSWORD=.*|SEAFILE_CACHE_PASSWORD=$cache_password|" .env
echo "administrator password: $admin_password"
unset admin_password db_root_password db_password jwt_key cache_password

The JWT key must be at least 32 characters. All five values must differ and must stay stable: the server stores its generated configuration inside the data volume and expects the same database and cache credentials on every start.

Set the public name before the first start:

sed -i "s|^SEAFILE_SERVER_HOSTNAME=.*|SEAFILE_SERVER_HOSTNAME=files.example.com|" .env
sed -i "s|^SEAFILE_ADMIN_EMAIL=.*|SEAFILE_ADMIN_EMAIL=you@example.com|" .env

Every .env variable:

  • SEAFILE_PORT is the local web port, default 8000;
  • SEAFILE_SERVER_HOSTNAME is the public host name without scheme or slash, and the setup script rejects localhost;
  • SEAFILE_SERVER_PROTOCOL is the scheme the service is reached by, https behind the proxy examples;
  • SEAFILE_ADMIN_EMAIL and SEAFILE_ADMIN_PASSWORD create the first account on the first start only;
  • SEAFILE_DB_ROOT_PASSWORD is the MariaDB root password, used for setup and for dumps;
  • SEAFILE_DB_PASSWORD is the password of the seafile database role;
  • SEAFILE_JWT_PRIVATE_KEY signs internal service tokens and must be 32 characters or longer;
  • SEAFILE_CACHE_PASSWORD protects the Valkey cache, which is not published to the network;
  • SEAFILE_DB_USER is the database role name, changed only before the first start;
  • SEAFILE_TIME_ZONE is an IANA time zone;
  • SEAFILE_DATA_VOLUME and SEAFILE_DB_VOLUME name the object-store and database volumes;
  • SEAFILE_BACKUP_DIR selects the host backup directory.

3. Start and sign in

docker compose config
docker compose pull
docker compose up -d --wait --wait-timeout 900
docker compose ps
curl --fail --silent --output /dev/null http://127.0.0.1:8000/accounts/login/

The first start is slow: the container creates ccnet_db, seafile_db, and seahub_db, generates the configuration into the data volume, and only then starts Seahub behind its own nginx. Sign in at the public address with SEAFILE_ADMIN_EMAIL, then install the desktop or mobile client and connect it to the same address.

VPS deployment

Keep 127.0.0.1:${SEAFILE_PORT}:80; only an HTTPS proxy on the host can reach the server. MariaDB and the cache have no published ports. Allow SSH, HTTP, and HTTPS through the firewall. Set SEAFILE_SERVER_PROTOCOL=https and create the DNS record before the first start: the value is written into the generated configuration, and clients that were configured with the wrong scheme keep using it.

Trusted LAN access

Prefer ssh -L 8000:127.0.0.1:8000 user@server. A permanent LAN deployment needs a resolvable host name — the setup script refuses localhost, so use a name your LAN DNS answers for, set SEAFILE_SERVER_PROTOCOL=http, replace the localhost bind with one specific private IP, and restrict the port with a firewall. Client-side encrypted libraries still protect their contents, but everything else, including session cookies, travels in clear text on that route.

Domain, HTTPS, and uploads

Set SEAFILE_SERVER_HOSTNAME=files.example.com and SEAFILE_SERVER_PROTOCOL=https, then replace the host in the Caddy, Nginx, or Traefik example. The proxy must not cap the request body — file blocks travel through the same origin, which is why the Nginx sample sets client_max_body_size 0 and disables request buffering — and must allow long transfers, so keep the read and send timeouts high. Changing the host name after the first start requires editing conf/ccnet.conf, conf/seahub_settings.py, and conf/seafile.conf inside the data volume.

Backup

chmod +x backup.sh restore.sh
./backup.sh

The script stops Seafile, dumps all three databases with mariadb-dump, archives the whole /shared volume with the object store, and stores .env and compose.yaml next to them. Both halves are needed: a database dump alone restores an index that points at file blocks that are no longer there. The archive contains every file and every secret; encrypt it and copy it off the server.

Restore

Restore irreversibly replaces the object store and all three databases. Use the same Seafile and MariaDB versions with the active .env:

./restore.sh ./backups/seafile-YYYYMMDDTHHMMSSZ.tar
curl --fail --silent --output /dev/null http://127.0.0.1:8000/accounts/login/

The script first backs up the state being replaced, recreates both volumes, unpacks the object store, imports the dump, and starts the server. Desktop clients that synced after the backup will re-upload their local changes; check one client before letting the rest reconnect. This procedure has not passed a practical restore test; rehearse it on a separate server first.

Update Seafile

Back up first and read the upstream upgrade notes for the exact version pair. Minor updates inside one major series are a tag change:

./backup.sh
docker compose pull
docker compose up -d --wait --wait-timeout 900
docker compose logs --tail=200 seafile

A major upgrade is not: Seafile runs versioned upgrade scripts and expects you to move one major version at a time, from the latest minor of the current series. Do not jump from 12.x to 14.x, and never change MariaDB in the same step. Replace the exact seafileltd/seafile-mc:13.0.25 tag with a reviewed version, never a -latest or -testing tag.

Rollback

Never start an older Seafile over databases a newer version has already upgraded. Restore the previous exact tag together with the pre-update archive:

docker compose down --timeout 120
./restore.sh ./backups/seafile-BEFORE-UPDATE.tar

After a failed MariaDB change, attach the old image to its preserved old volume, or import a compatible dump into an empty volume.

Stop and remove

docker compose down preserves both volumes. After verifying an off-server backup, remove everything irreversibly:

docker compose down
docker volume rm seafile-data seafile-database
rm -rf ~/services/seafile

Substitute actual names when the volume variables differ. Desktop clients keep their own local copies of every synced library; remove them separately if the data must be gone everywhere.

Sources: Seafile CE with Docker, upstream compose file, backup and recovery, and OAuth authentication.

Troubleshooting

The first start fails with “is not a valid ip or domain”

The setup script validates SEAFILE_SERVER_HOSTNAME and rejects localhost and anything without a dot. Set a real host name — the public domain, or a name your LAN DNS resolves — then remove the half-initialised volume and start again:

docker compose down --timeout 120
docker volume rm seafile-data seafile-database
docker compose up -d --wait --wait-timeout 900

Only do this while the installation is empty; on a working instance the same command destroys every library.

The container stays unhealthy for several minutes

The first start creates three databases, generates the configuration into the data volume, and then starts Seahub, which is why the health check allows a five minute start period. Watch the progress instead of restarting:

docker compose logs --follow seafile
docker compose ps
docker inspect --format '{{json .State.Health}}' "$(docker compose ps -q seafile)"

502 from the published port during that window is normal: nginx is up before Seahub is.

Seahub cannot reach the database or the cache

docker compose logs --tail=200 db cache
docker compose exec db mariadb --user=root --password="$SEAFILE_DB_ROOT_PASSWORD" -e "SHOW DATABASES;"

The credentials are written into conf/seafile.conf and conf/seahub_settings.py inside the data volume during setup. Changing SEAFILE_DB_PASSWORD or SEAFILE_CACHE_PASSWORD in .env afterwards does not rewrite those files, so either restore the old value or edit the generated configuration to match.

SEAFILE_SERVER_HOSTNAME and SEAFILE_SERVER_PROTOCOL are baked into the generated configuration at first start. To move to another domain, edit conf/ccnet.conf (SERVICE_URL), conf/seahub_settings.py (FILE_SERVER_ROOT, SERVICE_URL), and conf/seafile.conf inside the volume, then restart:

docker compose exec seafile grep -R "example.com" /shared/seafile/conf
docker compose restart seafile

Uploads fail at a certain size or time out

The proxy is the usual cause: the samples set client_max_body_size 0, disable request buffering, and raise the read and send timeouts because file blocks pass through the same origin. A 413 comes from the proxy, a 500 after several minutes usually means the timeout fired mid-transfer.

Downloads or uploads return 400 from /seafhttp/

That path is the Go file server behind the same nginx, and it answers only with a valid token, so a bare request returning 400 is expected. A real failure shows in the logs:

docker compose exec seafile tail -n 100 /shared/logs/seafile/seafile.log
docker compose exec seafile tail -n 100 /shared/logs/seahub/seahub.log

An encrypted library cannot be opened

Client-side encrypted libraries are decrypted with a password the server never stores. There is no administrative recovery: a lost library password means the data stays encrypted, even though the blocks are present in the backup.

The reverse proxy returns 502

On the host, run curl -I http://127.0.0.1:8000/accounts/login/. If that answers, the proxy is looking at the wrong upstream: inside a proxy container 127.0.0.1 is the proxy itself, so use a host gateway address or a shared Docker network. If it does not answer, start from the health-check section above.

Official sources

Latest release: 2026-07-13 · GitHub Stars: 15,195 · metadata checked Sep 2, 2026, 8:10 PM UTC

Disclaimer

Fossary is an independent informational catalog and is not affiliated with the developers of the listed applications. We collect information from public sources and verify published recipes, but we do not develop, distribute, or control these applications.

A listing or validation status is not a requirement, endorsement, or personal recommendation to use an application. You decide whether it is suitable and use it at your own risk. We do not warrant an application's security, availability, or fitness for purpose and, to the extent permitted by law, are not liable for resulting loss or damage. Review its license, security settings, data processing, and backup procedures before installation.