Skip to content
dionysius edited this page Sep 24, 2026 · 1 revision

Initial Setup

Configuration Files

All configuration lives under /etc/seafile/, mode 0500 owned by seafile:seafile (only the service user and root can read it):

  • seafile.env - secrets, database connection, cache, hostname, feature toggles. Loaded as environment variables by every seafile systemd service.
  • seafile.conf - seaf-server/file server tuning (upload limits, quotas, history retention, library trash expiry).
  • seahub_settings.py - Seahub (Django) settings: email, self-registration, session policy, thumbnails, and more.
  • gunicorn.conf.py - the gunicorn server running Seahub.

These are managed with ucf, so local edits survive package upgrades (a three-way merge, same as dpkg's own conffile handling).

Database Setup

Seafile needs three MySQL/MariaDB databases: ccnet_db, seafile_db, seahub_db. The server refuses to start until these and the connection details in seafile.env are configured.

Create a dedicated user, then either let the package create the databases for you, or create them yourself:

Option A - grant CREATE, let the package set up the databases:

CREATE USER 'seafile'@'127.0.0.1' IDENTIFIED BY 'choose-a-strong-password';
GRANT ALL PRIVILEGES ON `ccnet_db`.* TO 'seafile'@'127.0.0.1';
GRANT ALL PRIVILEGES ON `seafile_db`.* TO 'seafile'@'127.0.0.1';
GRANT ALL PRIVILEGES ON `seahub_db`.* TO 'seafile'@'127.0.0.1';
FLUSH PRIVILEGES;

A database-scoped GRANT ALL PRIVILEGES works even before the database exists - MySQL/MariaDB records it, and the grantee can then CREATE DATABASE that specific name. The first start of seafile.service creates the databases and loads the schema automatically.

Option B - create the databases yourself (if you'd rather not grant CREATE to the application user):

CREATE DATABASE ccnet_db CHARACTER SET utf8mb4;
CREATE DATABASE seafile_db CHARACTER SET utf8mb4;
CREATE DATABASE seahub_db CHARACTER SET utf8mb4;
CREATE USER 'seafile'@'127.0.0.1' IDENTIFIED BY 'choose-a-strong-password';
GRANT ALL PRIVILEGES ON ccnet_db.* TO 'seafile'@'127.0.0.1';
GRANT ALL PRIVILEGES ON seafile_db.* TO 'seafile'@'127.0.0.1';
GRANT ALL PRIVILEGES ON seahub_db.* TO 'seafile'@'127.0.0.1';
FLUSH PRIVILEGES;

Either way, set the connection details in seafile.env:

sudo -e /etc/seafile/seafile.env
SEAFILE_MYSQL_DB_HOST=127.0.0.1
SEAFILE_MYSQL_DB_PORT=3306
SEAFILE_MYSQL_DB_USER=seafile
SEAFILE_MYSQL_DB_PASSWORD=<the password you chose above>

The SEAFILE_MYSQL_DB_CCNET_DB_NAME/SEAFILE_MYSQL_DB_SEAFILE_DB_NAME/SEAFILE_MYSQL_DB_SEAHUB_DB_NAME variables only need changing if you used different database names than the defaults shown above.

Generate Secrets

Two secrets must be set before Seahub will start, both 32+ byte random hex strings:

openssl rand -hex 32
  • JWT_PRIVATE_KEY in seafile.env - signs the tokens exchanged between seaf-server, the file server, the notification server and Seahub. Changing it later invalidates all running sessions between components.
  • SECRET_KEY in seahub_settings.py - Django's secret key.

Start the Services

sudo systemctl restart seafile seafile-fileserver seafile-notification seahub

seafile-migrate.service runs automatically before seafile/seahub start - it creates the database schema on first start (or applies schema upgrades after a package upgrade) and is safe to re-run on every start; it's a no-op once the data is already at the installed version.

Create the first admin account once the services are up - see Admin Tools.

Basic Configuration

Network Access (Reverse Proxy)

Every service binds to 127.0.0.1 only - a reverse proxy is required for clients to reach the server. Route:

Path Backend Notes
/ 127.0.0.1:8000 Seahub (gunicorn)
/seafhttp 127.0.0.1:8082 File server (upload/download)
/notification 127.0.0.1:8083 Notification server - needs WebSocket (Upgrade/Connection headers) proxying

A minimal nginx starting point (adjust for your domain/TLS setup, and see the official manual's nginx guide for the complete reference configuration):

location / {
    proxy_pass http://127.0.0.1:8000;
    proxy_set_header X-Forwarded-For $remote_addr;
}
location /seafhttp {
    proxy_pass http://127.0.0.1:8082;
    client_max_body_size 0;
}
location /notification {
    proxy_pass http://127.0.0.1:8083;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

NOTIFICATION_SERVER_URL in seafile.env must be set to the public URL clients reach /notification through (e.g. wss://your.domain/notification) - client notifications stay inactive while it's unset.

Service Management

Unit Purpose
seafile.service seaf-server - the core storage/sync daemon
seafile-fileserver.service Go file server (upload/download); only runs while ENABLE_GO_FILESERVER=true in seafile.env
seafile-notification.service Real-time notification server; only runs while ENABLE_NOTIFICATION_SERVER=true
seahub.service The web frontend
seafile-migrate.service Database schema init/upgrade one-shot; not meant to be managed directly
seafile-fuse.service Optional read-only FUSE mount (seafile-fuse package only), disabled by default

For a headless server, disable seahub:

sudo systemctl disable --now seahub

Advanced Configuration

Administrative Commands

See Admin Tools for seaf-gc (garbage collection), seaf-fsck (integrity check/repair), and seahub-reset-admin (create/reset an admin account).

Modifying Systemd Units

Don't edit the shipped unit files directly - use systemctl edit <unit> to add a drop-in override instead, so your changes survive package upgrades. Every unit's own comments document the sandboxing options and what each StateDirectory/ConfigurationDirectory entry is for.

Next Steps

See Upgrading for what happens automatically on a package upgrade, and Admin Tools for day-to-day maintenance commands.

Troubleshooting

Services refuse to start with "not configured yet"

seafile-migrate checks SEAFILE_MYSQL_DB_PASSWORD, JWT_PRIVATE_KEY and SECRET_KEY before doing anything. Check journalctl -u seafile-migrate for exactly which one is still missing.

Access denied creating the database

If you used Option A above but the grant didn't take effect, double check you ran FLUSH PRIVILEGES and that the grant's host part ('seafile'@'127.0.0.1') matches SEAFILE_MYSQL_DB_HOST exactly - a mismatch (e.g. localhost vs 127.0.0.1) is treated as a different user by MySQL/MariaDB.