Seafile is a battle-tested open-source file sync and share platform used by universities, enterprises, and privacy-conscious teams as a self-hosted alternative to Dropbox or Google Drive. Hosting Seafile on your own server means no per-user billing, no vendor lock-in, and complete control over your data residency and retention policies. This guide walks through a production-grade deployment on Ubuntu 22.04 using Docker Compose, Caddy as the TLS-terminating reverse proxy, and MariaDB as the relational storage backend. You will end with a fully operational, HTTPS-accessible Seafile instance with persistent volumes, a least-privilege database user, and daily backup hooks.
Architecture and flow overview
The stack consists of four containers managed by a single Docker Compose file:
- db — MariaDB 10.11 storing Seafile metadata, user accounts, share records, and file commit trees.
- memcached — In-memory cache that accelerates Seahub (the Django web frontend) and reduces MariaDB query load.
- seafile — The combined Seafile server and Seahub application container, exposed on an internal port only.
- caddy — The reverse proxy that handles HTTPS certificate issuance via Let's Encrypt ACME and forwards HTTP/S traffic to the Seafile container.
Caddy listens on ports 80 and 443 on the host. All containers share a private seafile_net Docker bridge network. MariaDB is never exposed on the host network. Seafile authenticates against MariaDB using a dedicated application user with least-privilege grants. Persistent data lives in named Docker volumes (seafile_data, mariadb_data) so container restarts are non-destructive. File libraries are stored in Seafile's internal object storage format inside the seafile_data volume.
Prerequisites
- Ubuntu 22.04 VPS with a public IPv4 (or IPv6) address
- A domain name with an
Arecord pointing to the server (e.g.,files.example.com) - Docker Engine 24+ and Docker Compose v2 installed (
docker compose version) - Ports 80 and 443 open in the host firewall (
ufw allow 80,443/tcp) - At least 2 GB RAM and 20 GB disk space (Seafile's memory footprint at idle is roughly 800 MB)
- An outbound SMTP relay or transactional email service if you need email verification and password reset
Step-by-step deployment
1) Create the project directory
mkdir -p /opt/seafile
cd /opt/seafile2) Create the secrets file
Store all credentials in a .env file. Never commit this file to version control. Generate strong random values with openssl rand -hex 32.
cat > /opt/seafile/.env << 'EOF'
MARIADB_ROOT_PASSWORD=change_me_root_strong
MARIADB_DATABASE=seafile
MARIADB_USER=seafile
MARIADB_PASSWORD=change_me_seafile_strong
[email protected]
SEAFILE_ADMIN_PASSWORD=change_me_admin_strong
SEAFILE_SERVICE_URL=https://files.example.com
SEAFILE_SECRET_KEY=change_me_64char_hex_value
EOF
chmod 600 /opt/seafile/.env3) Write the Docker Compose file
version: "3.9"
services:
db:
image: mariadb:10.11
restart: unless-stopped
env_file: .env
environment:
MYSQL_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD}
MYSQL_DATABASE: ${MARIADB_DATABASE}
MYSQL_USER: ${MARIADB_USER}
MYSQL_PASSWORD: ${MARIADB_PASSWORD}
volumes:
- mariadb_data:/var/lib/mysql
networks:
- seafile_net
memcached:
image: memcached:1.6-alpine
restart: unless-stopped
command: memcached -m 256
networks:
- seafile_net
seafile:
image: seafileltd/seafile-mc:10.0-latest
restart: unless-stopped
env_file: .env
environment:
DB_HOST: db
DB_ROOT_PASSWD: ${MARIADB_ROOT_PASSWORD}
SEAFILE_ADMIN_EMAIL: ${SEAFILE_ADMIN_EMAIL}
SEAFILE_ADMIN_PASSWORD: ${SEAFILE_ADMIN_PASSWORD}
SEAFILE_SERVER_LETSENCRYPT: "false"
SEAFILE_SERVER_HOSTNAME: files.example.com
SEAFILE_SERVICE_URL: ${SEAFILE_SERVICE_URL}
SEAFILE_SECRET_KEY: ${SEAFILE_SECRET_KEY}
MEMCACHE_HOST: memcached
MEMCACHE_PORT: "11211"
volumes:
- seafile_data:/shared
depends_on:
- db
- memcached
networks:
- seafile_net
caddy:
image: caddy:2-alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
depends_on:
- seafile
networks:
- seafile_net
networks:
seafile_net:
driver: bridge
volumes:
mariadb_data:
seafile_data:
caddy_data:
caddy_config:4) Write the Caddyfile
files.example.com {
reverse_proxy seafile:80
encode gzip
header {
Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
X-Content-Type-Options nosniff
X-Frame-Options SAMEORIGIN
}
}Replace files.example.com with your actual domain in both the Caddyfile and the Compose environment variables.
5) Start the stack
docker compose up -d
docker compose logs -f seafileThe first start takes 60–120 seconds. Seafile initialises databases, runs migrations, and starts Seahub. Wait until the logs show Seahub started before opening the browser.
6) Open the browser and verify
Navigate to https://files.example.com and log in with the admin credentials from your .env file. You should see the Seafile dashboard with 0 files and 0 libraries.
Configuration and secrets handling
After the first start, Seafile writes its runtime configuration to /shared/seafile/conf/ inside the seafile_data volume. The three key files are:
seafile.conf— storage quotas, file size limits, WebDAV settingsseahub_settings.py— Django settings, SMTP, authentication backends, 2FAccnet.conf— server identity, network binding
To configure outbound email for password resets and share notifications, edit seahub_settings.py inside the volume:
docker compose exec seafile bash -c "cat >> /shared/seafile/conf/seahub_settings.py" << 'EOF'
EMAIL_USE_TLS = True
EMAIL_HOST = 'smtp.example.com'
EMAIL_HOST_USER = '[email protected]'
EMAIL_HOST_PASSWORD = 'your_smtp_password'
EMAIL_PORT = 587
DEFAULT_FROM_EMAIL = '[email protected]'
SERVER_EMAIL = '[email protected]'
EOFThen restart the seafile container to pick up the change:
docker compose restart seafileSecurity best practices:
- Never store SMTP passwords in Compose environment; write them directly to
seahub_settings.pyinside the named volume and restrict file permissions (chmod 600). - Rotate
SEAFILE_SECRET_KEYby updating the value in.envand inseahub_settings.pysimultaneously; mismatched keys invalidate all active sessions. - Use a read-only MariaDB user for Seafile's application queries — grant only
SELECT, INSERT, UPDATE, DELETEon theseafiledatabase, notGRANT OPTIONorALL. - Back up
seafile_dataandmariadb_datavolumes daily using a tool like Kopia or Restic before any upgrades.
Verification
Run these checks to confirm the deployment is healthy:
# All containers running
docker compose ps
# Caddy issued a valid TLS certificate
curl -I https://files.example.com | grep HTTP
# Seahub is reachable and returns a 200 or 302
curl -sL -o /dev/null -w "%{http_code}" https://files.example.com/
# MariaDB reachable from seafile container
docker compose exec seafile mysql -h db -u seafile -p"${MARIADB_PASSWORD}" -e "SHOW DATABASES;" 2>/dev/null | grep seafile
# Upload a test file via the CLI API
curl -sX POST https://files.example.com/api2/auth-token/ \
-d "[email protected]&password=your_admin_password" | python3 -m json.toolThe final command returns a token object if authentication is working. A successful token response confirms the REST API, Seahub, and the database are all operational end-to-end.
Common issues and fixes
Seahub not starting — database connection refused
Seafile's container starts immediately but Seahub waits for MariaDB to be ready. If MariaDB takes more than 30 seconds, Seafile may log OperationalError: (2003, "Can't connect to MySQL server"). Add a healthcheck to the db service and a condition: service_healthy in seafile.depends_on to serialize startup correctly.
TLS certificate not issued — ACME challenge fails
Caddy performs HTTP-01 ACME challenges on port 80. If your firewall blocks port 80 or the domain's DNS A record does not resolve to the server's IP, certificate issuance will fail silently. Verify with curl http://files.example.com/.well-known/acme-challenge/test from an external machine before investigating Caddy logs.
File upload fails with 413 Request Entity Too Large
Caddy has no built-in upload size limit for reverse-proxied requests, but Seafile's Nginx internal sub-server (inside the container) limits request bodies to 200 MB by default. Increase the limit in /shared/seafile/conf/seafile.conf:
[fileserver]
max_upload_size = 10000The value is in MB. Restart the container after the change.
Memcached connection errors in Seahub logs
If you see ConnectionRefusedError: [Errno 111] Connection refused when Seahub tries to reach Memcached, verify both containers are on the same Docker network with docker network inspect seafile_seafile_net and that the MEMCACHE_HOST environment variable matches the Compose service name exactly (memcached).
Admin password forgotten
Reset the admin password via the Seafile management command inside the running container:
docker compose exec seafile /opt/seafile/seafile-server-latest/reset-admin.shFAQ
Can I use Seafile with Active Directory or LDAP?
Yes. Seafile Community Edition supports LDAP authentication out of the box. Add the LDAP connection parameters to seahub_settings.py (server URL, base DN, bind credentials, and attribute mappings). Seafile Pro adds LDAP group sync and role-based access. The LDAP configuration does not require a container restart if you use docker compose exec seafile python3 manage.py check to validate before reloading.
How do I upgrade Seafile to a new major version?
Pull the new image tag, stop the stack, update the image reference in docker-compose.yml, and start the stack again. Seafile's container entrypoint detects the version bump and runs the appropriate database migration scripts automatically. Always back up seafile_data and mariadb_data volumes before upgrading. Skipping major versions (e.g., 9 → 11) is not supported; upgrade sequentially.
What is the difference between Seafile Community and Pro editions?
The Community Edition is fully open-source and free for any number of users. The Pro Edition adds full-text search inside document content, audit logging, LDAP group sync, Office Online/OnlyOffice integration, two-factor TOTP enforcement at the organisation level, and enhanced compliance export tools. For most self-hosted teams under 50 users, Community Edition is sufficient. Pro requires a licence key but is free for up to 3 users.
How do I enable WebDAV for desktop client mounts?
WebDAV is enabled by default in Seafile. Mount the endpoint at https://files.example.com/seafdav/ using any WebDAV-capable desktop client (Finder, Windows Explorer via Map Network Drive, or Dolphin). Set the path to /seafdav/ and authenticate with your Seafile username and password. For large file transfers, the Seafile native sync client is faster than WebDAV because it uses its own delta-sync protocol.
Can I migrate an existing Seafile instance to this Docker Compose setup?
Yes. Export your existing libraries with seaf-fsck and seaf-export, copy the /shared/seafile/ directory tree to the new volume, dump the MariaDB database, and import it into the new container's database. Run seaf-fsck.sh --repair after the data copy to detect and fix any block-level corruption introduced during the transfer. Test restores in a staging environment before cutting over production traffic.
How should I back up Seafile reliably?
A consistent Seafile backup requires two coordinated snapshots: a MariaDB dump (for metadata) and a file-system snapshot of the seafile_data volume (for file blocks). Take the database dump first while Seafile is running, then snapshot the volume. Restore in the reverse order: volume first, then import the SQL dump. Use Kopia, Restic, or Borgbackup scheduled via systemd timers or Docker labels with Ofelia to automate daily off-site backups.
Internal links
- Production Guide: Deploy Nextcloud with Docker Compose + NGINX + MariaDB + Redis on Ubuntu
- Production Guide: Deploy Vaultwarden with Docker Compose + Caddy on Ubuntu
- Production Guide: Deploy MinIO with Docker Compose + Caddy on Ubuntu
Talk to us
If you want this deployed with hardened access controls, monitoring standards, and production runbooks tailored to your environment, our team can help end-to-end.