Seafile 12 non-root domain

Hi!

I am trying to run seafile 12.0 (fresh docker container, as a docker compose) from ubuntu 24 LTS at a non-root domain behind a traefik reverse proxy. I tried applying the modifications outlined in the installation instructions for seafile 11, but could not manage to get it to work (fully).

My current docker-compose file (see end of the post) tries to provide seafile at ${DOMAIN_NAME}/cloud - but fails to do so.

I have tried a couple of configurations and modifications:

  1. Strip the subpath from the url using traefiks strip-prefix middleware, do not modify the nginx config and update the seahub_settings.py according to the 11.0 documentation
  2. Do not strip the subpath from the url, do not modify the nginx config and update seahub_settings.py according to the 11.0 documentation
  3. Do not strip the subpath from the url, modify the nginx config and update the seahub_settings.py according to the 11.0 documentation

For 1, 2, and 3 I modified seahub_settings.py to various degrees (e.g., by only updating media and site_root, up to the full example). Some of the configurations provided me at least with the seafile logos, and an error message that seafile could not be found

For 2 and 3, I also tried to add a rewrite rule to nginx that would strip the /cloud subpath from the request and then process it again - this did not work at all.

Frankly, I am at a loss for any further modifications and would be very glad if anybody can give me some pointers in the right direction. Unfortunately, it is not a possibility to provide seafile at a subdomain (e.g., cloud.${DOMAIN_NAME}) for various (company) reasons. I am more than happy to submit a PR to the documentation to update it with steps on how to get non-root domains working (and/or together with traefik) once/if I get this issue fixed.

Finally, while trying to solve the non-root domain problem I think I may have found a bug: It seems that the served html document by seafile (the few times that I managed), requests the jquery javascript from a hardcoded root domain /media… instead of honoring the updated MEDIA_URL provided in seahub_settings.py. Maybe that is just a product of my missconfiguration - but every other resource used the correct URL. This is also why there is the exposed /media subdomain.

Thanks for any help and/or advice in advance!

Docker compose:

services:
  seafile-db:
    image: ${SEAFILE_DB_IMAGE:-mariadb:10.11}
    container_name: seafile-mysql
    environment:
      - MYSQL_ROOT_PASSWORD=${SEAFILE_SQL_ROOT_PASSWD}
      - MYSQL_LOG_CONSOLE=true
      - MARIADB_AUTO_UPGRADE=1
    volumes:
      - ${STORAGE_PATH}/seafile/db:/var/lib/mysql
      - /etc/localtime:/etc/localtime:ro
    networks:
      - seafile-net
    healthcheck:
      test:
        [
          "CMD",
          "/usr/local/bin/healthcheck.sh",
          "--connect",
          "--mariadbupgrade",
          "--innodb_initialized",
        ]
      interval: 20s
      start_period: 30s
      timeout: 5s
      retries: 10

  seafile-memcached:
    image: ${SEAFILE_MEMCACHED_IMAGE:-memcached:1.6.29}
    container_name: seafile-memcached
    entrypoint: memcached -m 256
    networks:
      - seafile-net

  seafile:
    image: ${SEAFILE_IMAGE:-seafileltd/seafile-mc:12.0-latest}
    container_name: seafile
    volumes:
      - ${STORAGE_PATH}/seafile/data:/shared
      - /etc/localtime:/etc/localtime:ro
    environment:
      - DB_HOST=${SEAFILE_MYSQL_DB_HOST:-seafile-db}
      - DB_PORT=${SEAFILE_MYSQL_DB_PORT:-3306}
      - DB_USER=${SEAFILE_MYSQL_DB_USER:-seafile}
      - DB_ROOT_PASSWD=${SEAFILE_SQL_ROOT_PASSWD}
      - DB_PASSWORD=${SEAFILE_MYSQL_DB_PASSWORD:?Variable is not set or empty}
      - SEAFILE_MYSQL_DB_CCNET_DB_NAME=${SEAFILE_MYSQL_DB_CCNET_DB_NAME:-ccnet_db}
      - SEAFILE_MYSQL_DB_SEAFILE_DB_NAME=${SEAFILE_MYSQL_DB_SEAFILE_DB_NAME:-seafile_db}
      - SEAFILE_MYSQL_DB_SEAHUB_DB_NAME=${SEAFILE_MYSQL_DB_SEAHUB_DB_NAME:-seahub_db}
      - TIME_ZONE=${TZ}
      # - INIT_SEAFILE_ADMIN_EMAIL=${INIT_SEAFILE_ADMIN_EMAIL:-me@example.com}
      # - INIT_SEAFILE_ADMIN_PASSWORD=${INIT_SEAFILE_ADMIN_PASSWORD:-asecret}
      - SEAFILE_SERVER_HOSTNAME=${DOMAIN_NAME}
      - SEAFILE_SERVER_PROTOCOL=https
      - SITE_ROOT=${SEAFILE_URL_SUBPATH}
      - NON_ROOT=${NON_ROOT:-false} # defines if seafile should run as non-root user
      - JWT_PRIVATE_KEY=${JWT_PRIVATE_KEY:?Variable is not set or empty}
      - SEAFILE_LOG_TO_STDOUT=${SEAFILE_LOG_TO_STDOUT:-false}
      - ENABLE_SEADOC=${ENABLE_SEADOC:-false}
      # - SEADOC_SERVER_URL=${SEADOC_SERVER_URL:-http://seafile.example.com/sdoc-server}
    labels:
      - "traefik.enable=true"

      - "traefik.http.routers.seafile.entrypoints=websecure"
      - "traefik.http.routers.seafile.rule=Host(`${DOMAIN_NAME}`) && PathPrefix(`${SEAFILE_URL_SUBPATH}`)"
      - "traefik.http.routers.seafile.service=seafile"
      - "traefik.http.services.seafile.loadbalancer.server.port=80"

      # Strip the SEAFILE_URL_SUBPATH from the request path
      - "traefik.http.middlewares.strip-seafile-path.stripprefix.prefixes=${SEAFILE_URL_SUBPATH}"
      - "traefik.http.routers.seafile.middlewares=strip-seafile-path"

      # Fix for loading the jquery-path which seems to be hardcoded in the served html
      - "traefik.http.routers.seafile-media-fix.rule=Host(`${DOMAIN_NAME}`) && PathPrefix(`/media`)"
      - "traefik.http.routers.seafile-media-fix.entrypoints=websecure"
      - "treafik.http.services.seafile-media-fix.loadbalancer.server.port=80"

      # - "traefik.http.routers.webdav.rule=Host(`${DOMAIN_NAME}`) && PathPrefix(`/seafdav`)"
      # - "traefik.http.routers.webdav.entrypoints=websecure"

      # - "traefik.http.routers.seafhttp.rule=Host(`${DOMAIN_NAME}`) && PathPrefix(`/seafhttp`)"
      # - "traefik.http.routers.seafhttp.entrypoints=websecure"
    depends_on:
      - seafile-db
      - seafile-memcached
    networks:
      - seafile-net
      - proxy

networks:
  seafile-net:
  proxy:
	# No need to mark it as external as my startup script merges this with the traefik configuration	

We will no longer support non-root domain officially. It is better if you switch to root domain.

That’s unfortunate. I have been configuring my nginx such that ‘/’ is the root of my own site, which contains links to different apps, e.g. ‘/app1’, ‘/app2’, and ‘/seafile’.

May I ask why non-root domains are no longer supported in Seahub?

This is for reducing maintenance and support cost.

Gotcha, thank you for the reply.

We will no longer support non-root domain officially. It is better if you switch to root domain.

That would be most unfortunate.

I have been configuring my nginx such that ‘/’ is the root of my own site, which contains links to different apps, e.g. ‘/app1’, ‘/app2’, and ‘/seafile’.

I totally agree. Especially if you host things at home as a private person and you only have one public IP available (at least as long as IPv4 is still around, and we are far from IPv6-only…), this is the easiest approach by far.
I know that there are also possibilities like name-based virtual hosts and content-rewriting on the reverse proxy, but these have their pitfalls and are more prune to configuration errors.

Me personally, I am also running a completely customized docker image* (without the internal nginx at all; the various seafile|seahub|seafevent ports are all exposed; serve_static is enabled together with a modified** urls.py) and only one front-end reverse proxy for everything.
However, I am currently still at 11.0.13 CE and have not yet gotten that far to adapt all changes in the scripts and files*** for my custom image for the current release 12. I will keep you posted once I find out more (or if non-root context is completely impossible now).

* https://hub.docker.com/r/nightshade78/seafile-urup-baseimage/tags

** see detailed changes to the urls.py at Server Migration: 6.0.7+Windows+MySQL to 6.3.4+Linux+MariaDB

*** GitHub - nightshade78/seafile-urup: Seafile Server UnRooted UnProxxed - a docker image for Seafile server modified for usage with a non-root web path and an external proxy