Deploying Seafile CE/Pro 11 with Docker and SWAG as a Reverse Proxy

After long days and countless hours of effort, I have finally found a configuration that allows Seafile to function seamlessly behind SWAG (Secure Web Application Gateway) as a reverse proxy. This guide is a message to the open-source community, sharing the fruits of my labor to help others achieve the same success.

Environment Information

  • Operating System: Ubuntu 24.04.1 LTS
  • Docker Version: 27.3.1, build ce12230
  • Seafile Community Edition/Pro : 11
  • SWAG Network Name: swag_default
  • Seafile Working Directory: /opt/seafile/
  • Stack Setup: SWAG is in one stack, and Seafile is in another stack.

Step 2: Prepare the Working Directory

I decided to place Seafile in

/opt/seafile/

, where the docker-compose will create the mysql and data directories.

Step 3: Create the docker-compose.yml File

  • Create and edit the docker-compose.yml file:
  • This can also be copy-pasted into Portainer if you prefer using a GUI.
   services:
     db:
       image: mariadb:10.11
       container_name: seafile-mysql
       environment:
         - MYSQL_ROOT_PASSWORD=db_dev
         - MYSQL_LOG_CONSOLE=true
         - MARIADB_AUTO_UPGRADE=1
       volumes:
         - /opt/seafile/mysql/db:/var/lib/mysql
       networks:
         - seafile-net

     memcached:
       image: memcached:1.6.18
       container_name: seafile-memcached
       entrypoint: memcached -m 256
       networks:
         - seafile-net
         
     seafile:
       image: seafileltd/seafile-mc:11.0-latest
       container_name: seafile
       volumes:
         - /opt/seafile/data:/shared
       environment:
         - DB_HOST=db
         - DB_ROOT_PASSWD=db_dev
         - TIME_ZONE=America/New_York
         - SEAFILE_ADMIN_EMAIL=me@domain.com
         - SEAFILE_ADMIN_PASSWORD=YourAdminPassword
         - SEAFILE_SERVER_LETSENCRYPT=false
         - SEAFILE_SERVER_HOSTNAME=seafile.domain.com
         - FORCE_HTTPS_IN_CONF=true
       depends_on:
         - db
         - memcached
       networks:
         - seafile-net
         - swag_default

   networks:
     seafile-net:
       driver: bridge
     swag_default:
       external: true
  • seafile-net: Internal bridge network for Seafile services
  • swag_default: External network for SWAG integration

This network configuration ensures that Seafile can communicate internally with its database and memcached services, while also being accessible externally through the SWAG reverse proxy.

Step 4: Configure Nginx as a Reverse Proxy

  • My SWAG configuration directory is located at /var/docker_data/swag. You can create the configuration file directly with:

    vim /var/docker_data/swag/nginx/proxy-confs/seafile.subdomain.conf

  • We create this file ourselves because no template is provided by SWAG for Seafile. Perhaps the one I provide here will be included in future versions of Seafile.

  • Copy and paste the following content:

server {
    listen 443 ssl;
    listen [::]:443 ssl;

    # Your domain name
    server_name seafile.domain.com;
    # Or like the official nginx template from SWAG
    # server_name seafile.*;

    # Include the SSL configuration from SWAG Template
    include /config/nginx/ssl.conf;

    # No limit for the uploads
    client_max_body_size 0;

    location / {
        # Use the internal Docker DNS resolver
        include /config/nginx/resolver.conf;

        # Proxy configuration to the Seafile container
        set $upstream_app seafile;
        # It is 80 and NOT 8080
        set $upstream_port 80;
        set $upstream_proto http;
        proxy_pass $upstream_proto://$upstream_app:$upstream_port;

        # Headers necessary for CSRF and other features
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;
        proxy_set_header Connection "";

        # For large files
        proxy_connect_timeout 36000s;
        proxy_read_timeout 36000s;
        proxy_send_timeout 36000s;

        # Support WebSocket
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

IMPORTANT:
The seafile.subdomain.conf does not include include /config/nginx/proxy.conf due to header conflicts.

EDIT 21/12/2024

I’ve significantly improved the Nginx configuration for Seafile behind SWAG:

  • Added crucial security headers (HSTS, CSP, Permissions Policy, etc.)
  • Optimized proxy settings for better performance
  • Ensured compatibility with Seafile’s requirements

Key changes:

  • Strict Transport Security (HSTS) enforcement
  • Content Security Policy (CSP) implementation
  • Referrer Policy and X-Frame-Options for enhanced protection
  • Optimized proxy settings for Seafile compatibility

This update provides a more secure and efficient setup for Seafile behind SWAG. Implement these changes to boost your server’s security posture.

server {
    listen 443 ssl;
    listen [::]:443 ssl;

    # Your domain name
    server_name seafile.domain.com;

    # Include SWAG's standard SSL configuration
    include /config/nginx/ssl.conf;

    add_header Strict-Transport-Security "max-age=63072000" always;
    add_header Cache-Control "no-transform" always;
    add_header Content-Security-Policy "upgrade-insecure-requests; frame-ancestors 'self'" always;
    add_header Permissions-Policy "interest-cohort=()" always;
    add_header Referrer-Policy "same-origin" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-UA-Compatible "IE=Edge" always;
    add_header X-XSS-Protection "1; mode=block" always;
    client_max_body_size 0;

    location / {
        # Utilise le resolver DNS interne de Docker
        include /config/nginx/resolver.conf;

        # Configuration du proxy vers le container Seafile
        set $upstream_app seafile;
        set $upstream_port 80;
        set $upstream_proto http;
        proxy_pass $upstream_proto://$upstream_app:$upstream_port;

        # Timeout if the real server is dead
        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;

        # Proxy connection settings
        proxy_buffers 32 4k;
        proxy_connect_timeout 36000s;
        proxy_headers_hash_bucket_size 128;
        proxy_headers_hash_max_size 1024;
        proxy_http_version 1.1;
        proxy_read_timeout 36000s;
        proxy_redirect http:// $scheme://;
        proxy_send_timeout 36000s;

        # Proxy cache and cookie settings
        proxy_cache_bypass $cookie_session;
        proxy_no_cache $cookie_session;

        # Client and request identification
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Original-Method $request_method;
        proxy_set_header X-Original-URL $scheme://$http_host$request_uri;

        # Protocol and server information
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Port $server_port;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-Forwarded-Ssl off;

        # Websocket, connection and upgrade management
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Upgrade $http_upgrade;

        # Host and proxy information
        proxy_set_header Host $http_host;
        proxy_set_header Proxy "";

        # Request and URI management
        proxy_set_header X-Forwarded-Uri $request_uri;
        proxy_set_header X-Forwarded-Method $request_method;

        # SSL optimization
        proxy_set_header Early-Data $ssl_early_data;

    }
}

Step 5: Configure Seafile

  • Modify seahub_settings.py:
  • Ensure the “s” is added after “http” in SERVICE_URL and verify that both SERVICE_URL and FILE_SERVER_ROOT are set to your domain name.
...
  SERVICE_URL = "https://seafile.domain.com"
...
  FILE_SERVER_ROOT = "https://seafile.domain.com/seafhttp"
  CSRF_TRUSTED_ORIGINS = ["https://seafile.domain.com"]
  • Why CSRF_TRUSTED_ORIGINS is Important:
  • CSRF (Cross-Site Request Forgery) is a security measure to prevent unauthorized actions on a web application. By adding CSRF_TRUSTED_ORIGINS, you inform Django (the framework used by Seafile) that requests from your domain are safe.
  • This configuration is crucial when Seafile is behind a reverse proxy, as it ensures that legitimate requests are not blocked.
  • These resources helped me understand this requirement:

Stack Overflow Explanation : 38842030/12317483

I can’t add link in post

Step 6: Restart the Stack

  • Restart the stack to apply all changes:
  docker compose down
  docker compose up -d
  • Everything should now function correctly. Access Seafile at https://seafile.domain.com and enjoy your setup!

Changelog:

21/12/2024:

  • Major security enhancement: Added crucial security headers
  • Implemented HSTS, CSP, Permissions Policy, and other protective measures
  • Optimized proxy settings for improved Seafile compatibility and performance
  • Updated X-Forwarded-* headers for better request handling

11/12/2024:

  • Added Cloudflare compatibility configuration
  • Included specific settings for proper functioning behind Cloudflare’s proxy

Great guide - I’ve had to transpose a lot of stuff to get it to work in unraid a bit, so I’m using some pre-made templates for mariadb, seafile and swag - I tried to use the compose file you set up above, but it just wasnt liking unraid for whatever reason!

I dont know if I’ve followed something wrong or set up something wrong, but I’m having a few issues accessing externally,

This is all from a complete fresh wipe of Unraid, formatted disks, and brand new installation usb, so there should be nothing else interfering with it!

I’ve detailed every step I’ve gone through below in case this helps anyone else that’s stuck anywhere along the way - and so anyone can point out where I’ve gone wrong!!! - I’ll try to update this as fixes are suggested, so that it could be used as a guide for anyone needing help,

Disclaimer - I have no idea what I’m doing here! this is all just picked up from guides etc. !!!

1.	Formatted all drives for a complete fresh start
2.	Setup new Unraid USB Key to get rid of any config files stored in there that might cause any issues from previous installs
3.	Unraid USB Settings:
•	Set to the most recent version of UnRaid
•	Name Changed from Default ‘Tower
•	Connection set to DHCP
4.	Startup server
5.	Login through IP in browser
6.	Activate key
7.	Set parity drive (Largest Available Drive), Set Array drives below Parity drive
8.	Add Pool -> Add Cache Drive
9.	Start array
10.	Cancel parity sync for the meantime to free up a bit more cpu usage and so it’s not an issue when I have to stop/start the array in a bit (just speeds things up) will activate again once everything is working
11.	Go to settings -> docker -> Disable Docker -> Apply -> enable user defined networks and re-enable docker -> Apply
12.	Console -> docker network create seafile-net
13.	Console -> docker network list   #to check network successfully created
14.	Apps -> install community applications plugin
15.	Apps -> install Dynamix File Manager (Just makes things easier!)
16.	Apps -> mariadb (linuxserver’s Repository) -> install
17.	Settings changed in the add container setup page:
•	Name changed to seafile-mariadb
•	Network type: Custom: seafile-net
•	MYSQL_ROOT_PASSWORD: set to password (DON’T LET GOOGLE AUTO-FILL THIS – IT DOESN’T WORK LATER ON AND YOU’LL HAVE TO START FROM THE BEGINNING!!!!)
•	MYSQL_USER: set to Admin
•	MYSQL_PASSWORD: set to password (DON’T LET GOOGLE AUTO-FILL THIS – IT DOESN’T WORK LATER ON AND YOU’LL HAVE TO START FROM THE BEGINNING!!!!)
18.	Apply and install – No errors shown  --  ‘The command finished successfully!’
19.	Apps -> seafile-11(dglb99’s Repository) -> install
20.	Settings changed in the add container setup page:
•	Network Type: Custom: seafile-net
•	Seafile hostname: seafile.mydomain.uk
•	SQL Root Password: Set to the password configured in mariadb in step 13
•	Admin email: set to my email address
•	Admin password: set to an admin password
21.	Apply and install – No Errors shown  --  ‘The command finished successfully!’
22.	Docker -> click on Seafile-11 Icon -> WebUI – All Working as it should be
Local Access Working Properly At This Point – Just Need To Get External Access Working  --

23.	Terminal -> sudo nano /mnt/user/seafile/seafile/conf seahub_settings.py
•	Change http to https on SERVICE_URL and FILE_SERVER_ROOT
•	Add: CSRF_TRUSTED_ORIGINS = ["https://seafile.mydomain.uk"]
24.	^S ^X
25.	Docker Tab -> Stop All 
26.	Docker Tab -> Start All
27.	Terminal -> cd /mnt/user/appdata/swag/nginx/proxf-confs
28.	Sudo nano seafile.subdomain.conf
29.	Paste in NGINX Reverse Proxy content
30.	Change:
•	Server_name seafile.mydomain.uk
31.	^S ^X
32.	Login to seafile WebUI with email and password previously set
33.	System admin in top right corner -> Settings
34.	Change SERVICE_URL http to https://seafile.mydomain.uk – same for FILE_SERVER_ROOT (Remember to click the tick on the right side of the textbox to apply them!)
35.	Back To UnRaid
36.	Apps -> Swag (Linuxserver’s Repository) ->install
37.	Settings Changed in the add container setup page:
•	Network Type: Custom: seafile-net
•	WebUI port: changed from 443 to 1443 to avoid UnRaid WebUI port
•	Port 80: Changed from 80 to 180 to avoid UnRaid WebUI port again
•	URL: seafile.mydomain.uk
•	Validation: DNS
•	Subdomains: wildcard
•	DNSplugin: cloudflare
•	Only_Subdomains: false
38.	Apply and Install
39.	Leaving UnRaid – going to Cloudflare.com
40.	Domain -> DNS -> Records 
41.	Add Record:
•	Type: A
•	Name: mydomain.uk
•	IPv4 address: set to address of server
•	Proxy Status: DNS Only
42.	Save
43.	Add Record:
•	Type: CNAME
•	Name: *
•	Target: @
44.	Save
45.	My Profile -> API Tokens -> Create Token -> Create Custom Token:
•	Name: SWAG
•	Permissions: Zone,DNS,Edit
46.	Continue to Summary -> Create Token -> Copy Token
47.	Back to my domain homepage -> SSL/TLS
48.	Set to Full -> Save
49.	Back To Unraid
50.	Terminal
51.	Nano /mnt/user/appdata/swag/dns-conf/cloudflare.ini
52.	Comment out DNS_Cloudflare_Email and DNS_Cloudflare_api_Key with #
53.	Uncomment DNS_Cloudflare_API_Token
54.	Paste API token into DNS_Cloudflare_API_Token
55.	^S ^X to save and exit
56.	Restart Swag
57.	Out of Unraid, into Router Settings
58.	Port forwarding:
•	IP Address: Server’s IP Address
•	Internal port Range: 80:80
•	External Port Range: 180:180

•	IP Address: Server’s IP Address
•	Internal Port Range: 443:443
•	External Port Range:1443:1443
59.	New browser tab: enter https://seafile.mydomain.uk/
60.	No result – DNS_PROBE_FINISHED_NXDOMAIN