Seafile/Seadoc Deployment Failing Behind Standalone Caddy (Uploads/Doc Viewing Broken)

Hi all!

I am running the official Seafile Community/MC 12.0 stack with Seadoc using Docker Compose on Ubuntu server. I am deploying the stack behind a separate, standalone reverse proxy Caddy v2.7.x container on an external network.

  • Seafile Domain: https://seafile.example.com

  • Docker Network: services_net (Shared by Caddy, Seafile, Seadoc, etc.)

  • Configuration Strategy: Single-Domain, Subpath (All traffic goes to one domain).

Problem Description (Three Consistent Failures)

The core web interface loads (login/library view works), but features relying on the File Server (:8082) or Seadoc (:8888) are broken, pointing to a severe routing/protocol misconfiguration. This is a common pattern for proxy issues.

  1. File Uploads (File Server: :8082): Files (of any type) fail with a “Network Error” after a brief delay. (This strongly suggests an issue with the /seafhttp route or the file server’s internal configuration/protocol).

  2. Document Viewing (PDFs): PDFs open in a new tab but load endlessly.

  3. Document Editing (DOCX/ODT): Fails instantly with the error message: “online view is not applicable to this file format.” (This suggests the Seadoc server is unreachable or misconfigured).

Configuration Details

1. Caddyfile (Single-Domain, Subpath)

This Caddyfile is used by the standalone caddy-proxy container. I am using handle_path for all subpath routing (/seafhttp/, /sdoc-server/) to ensure correct path stripping before proxying.

seafile.example.com {

    # 1. File Server (Uploads, Downloads, PDFs) - Target: seafile:8082
    handle_path /seafhttp/* {
        reverse_proxy seafile:8082 {
            header_up Host {host}
            header_up X-Forwarded-Proto {scheme}
            # CRITICAL: Mandatory for internal HTTP connection to 8082
            transport http {
                tls_insecure_skip_verify
            }
        }
    }

    # 2. Seadoc WebSockets (Real-time connection) - Target: seadoc:8888
    handle_path /sdoc-server/socket.io* {
        reverse_proxy seadoc:8888 {
            # Mandatory WebSocket headers
            header_up Connection {http.request.header.Connection}
            header_up Upgrade {http.request.header.Upgrade}
            header_up Host {host}
            header_up X-Forwarded-Proto {scheme}
        }
    }

    # 3. Seadoc API/Assets (Document viewing) - Target: seadoc:8888
    handle_path /sdoc-server/* {
        reverse_proxy seadoc:8888 {
            header_up Host {host}
            header_up X-Forwarded-Proto {scheme}
        }
    }

    # 4. Main Seafile Web Interface (Catch-all) - Target: seafile:80
    reverse_proxy seafile:80 {
        header_up Host {host}
        header_up X-Forwarded-Proto {scheme}
    }

    request_body {
        max_size 0
    }
}

2. Seafile Environment Variables (.env)

COMPOSE_FILE='seafile-server.yml,seadoc.yml'
COMPOSE_PATH_SEPARATOR=','

SEAFILE_IMAGE=seafileltd/seafile-mc:12.0-latest
# ... other images ...

SEAFILE_VOLUME=/mnt/data-storage/seafile/seafile-data
# ... other volumes ...

# ... DB credentials ...

TIME_ZONE=Etc/UTC

JWT_PRIVATE_KEY=vc2944qnLRamBID648dMos5tbZXFjkl3DiTP0LBj

# --- CRITICAL PROXY SETTINGS ---
SEAFILE_SERVER_HOSTNAME=seafile.example.com 
SEAFILE_SERVER_PROTOCOL=https
# -------------------------------

INIT_SEAFILE_ADMIN_EMAIL=me@example.com # Replaced email
INIT_SEAFILE_ADMIN_PASSWORD=H0meserver_p@ss

SEADOC_IMAGE=seafileltd/sdoc-server:1.0-latest
SEADOC_VOLUME=/opt/seadoc-data

ENABLE_SEADOC=true
# ... other notification variables ...

3. Seafile Docker Compose (seafile-server.yml)

The specific Environment Variables are set directly, overriding any defaults, using the Single-Domain, Subpath configuration.

services:
  db:
    # ... MariaDB config ...
    networks:
      - services_net
    healthcheck:
      # ... healthcheck config ...
    restart: unless-stopped

  memcached:
    # ... memcached config ...
    networks:
      - services_net
    restart: unless-stopped

  seafile:
    image: ${SEAFILE_IMAGE:-seafileltd/seafile-mc:12.0-latest}
    container_name: seafile
    # ports: # COMMENTED OUT, as caddy handles external ports
    volumes:
      - ${SEAFILE_VOLUME:-/opt/seafile-data}:/shared
    environment:
      # ... DB config ...
      # ... Admin config ...
      
      - SEAFILE_SERVER_HOSTNAME=${SEAFILE_SERVER_HOSTNAME:?Variable is not set or empty}
      - SEAFILE_SERVER_PROTOCOL=${SEAFILE_SERVER_PROTOCOL:-http}
      # ... other defaults ...
      - ENABLE_SEADOC=${ENABLE_SEADOC:-true}
      
      # --- CRITICAL STANDALONE PROXY VARIABLES ---
      - FILE_SERVER_ROOT=https://seafile.example.com/seafhttp 
      - SEADOC_SERVER_URL=https://seafile.example.com/sdoc-server
      - FORCE_HTTPS_IN_CONF=true
      # ------------------------------------------

    depends_on:
      # ... dependencies ...
    networks:
      - services_net
    restart: unless-stopped

networks:
  services_net:
    external: true

4. Seadoc Docker Compose (seadoc.yml)

Seadoc’s service URL correctly points to the internal Docker service name of Seafile (http://seafile).

services:
  seadoc:
    image: ${SEADOC_IMAGE:-seafileltd/sdoc-server:1.0-latest}
    container_name: seadoc
    volumes:
      - ${SEADOC_VOLUME:-/opt/seadoc-data/}:/shared
    # ports: # COMMENTED OUT, as caddy handles external ports
    environment:
      # ... DB config ...
      - JWT_PRIVATE_KEY=${JWT_PRIVATE_KEY:?Variable is not set or empty}
      
      # --- CRITICAL INTERNAL URL ---
      - SEAHUB_SERVICE_URL=${SEAFILE_SERVICE_URL:-http://seafile}
      # -----------------------------

    depends_on:
      db:
        condition: service_healthy
    networks:
      - services_net
    restart: unless-stopped

networks:
  services_net:
    external: true

5. Caddy Proxy Docker Compose (caddy.yml)

This defines the external Caddy proxy container on the shared network.

services:
  caddy:
    image: caddy:latest
    container_name: caddy-proxy
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config
    networks:
      - services_net

networks:
  services_net:
    name: services_net #creating and managing network for all services

volumes:
  caddy_data:
  caddy_config:

Reference Documentation

The configuration strategy used here is based on the official Seafile documentation for reverse proxy setups:

  • Seafile Admin Manual: Use other reverse proxy: https://manual.seafile.com/12.0/setup/use_other_reverse_proxy/

Request for Help

  1. Is there any required Caddy directive I am missing in the reverse_proxy or handle_path blocks (e.g., specific buffering, path rewrite, or header) that is essential for the single-domain subpath configuration?

  2. Could the transport http { tls_insecure_skip_verify } block for seafile:8082 be placed incorrectly, or is there an alternative, safer way to bypass the internal SSL check for port 8082?

  3. Are there any known hidden environment variables for Seafile/Seadoc that must be set when migrating from the built-in Caddy proxy to a standalone proxy? (We have tried the common FILE_SERVER_ROOT, SEADOC_SERVER_URL, and FORCE_HTTPS_IN_CONF).

It is better if you can use 13.0 version, as the deployment of seadoc has been much improved in 13.0.

For standalone caddy, I recommend you check this document: Use other reverse proxy - Seafile Admin Manual

this is exactly doc i used for my setup.
the thing is that i can brows my library without any issues, i can’t upload/download and open files though.

In your post, you said you were using the 12.0 document:

Seafile Admin Manual: Use other reverse proxy:*https://manual.seafile.com/12.0/setup/use_other_reverse_proxy/

You can check the 13.0 document.

yeah, you are right, i’ve tried to use 12v.

having briefly reviewed the link it is suggesting almost the same as for 12v.

I’ve been working a bit with my config based on the seafile admin manual. There are ngnix config example for the standalone reverse_proxy, so i tried to adapt it to my caddy reverse proxy run as container. I got rid of almost all redundant directives as well as ‘/seafhhtp/*’ section at all, and managed to have uploading works: i can upload/download files, sync folders, open pdf files (seems it downloads them and open in browser, don’t it?). Viewing .odt or .docx still doesn’t work though, apparently seadoc caddy section isn’t correct.

here is current Caddyfile:

# 1.1 Configuration for the main Seafile app:
#-------------------------------------------------------------------------------------------------------------------

seafile.example.com {

    # --- Main Seafile web interface
    reverse_proxy seafile:80

# 1.2 Configuration for Seadoc/Office features (WReal-time doc editing and API)
#-------------------------------------------------------------------------------------------------------------------

    # --- Route 1: WebSockets for real-time editing (socket.io)
    handle /socket.io {
        request_body {
            max_size 100MB
        }
        reverse_proxy seadoc:80 {
            header_up Ngnix-Proxy true
        }
    }

    # --- Route 2: Seadoc Server Path (for static assets and API calls)
    handle /sdoc-server/* {
        request_body {
            max_size 100MB
        }
        reverse_proxy seadoc:80 {
            header_up X-Forwaeded-Host {http.host}
        }
    }

}

i believe i could managed it by translate ngnix config given in documentation into caddy syntax and setting up ports correctly, so may be someone can help.