分享一下Seafile 13 使用 Docker 部署,但不使用官方默认的 Caddy,而是使用外部 Nginx 或 Nginx Proxy Manager 统一做 HTTPS 反向代理。
适合这种网络架构:
公网用户
-> HTTPS + 域名 + 端口
-> 网关 / Nginx / Nginx Proxy Manager
-> 内网 Seafile Docker 服务器
例如:
公网访问地址:
https://cloud.example.com:8443
Nginx Proxy Manager 所在机器:
192.168.1.10
Seafile Docker 所在机器:
192.168.1.20
实际使用时,请把本文中的:
cloud.example.com:8443
192.168.1.20
替换成你自己的域名、端口和内网 IP。
一、为什么可以不用 Caddy?
Seafile 13 Docker 默认会带 Caddy,用来自动处理 HTTPS 和反向代理。
但如果你的网络里已经有统一入口,比如:
Nginx
Nginx Proxy Manager
OpenResty
Traefik
HAProxy
那么就可以不用 Seafile 自带的 Caddy。
去掉 Caddy 后,Seafile 服务器上不需要额外安装宿主机 Nginx 或 Apache,因为 Seafile 容器自己已经提供 HTTP 服务。
你只需要把容器端口映射到宿主机,然后让外部 Nginx 代理过去。
最终关系类似这样:
NPM / Nginx
-> http://192.168.1.20:80 -> Seafile 主服务
-> http://192.168.1.20:8888 -> SeaDoc 在线文档服务
数据库、Redis 不需要暴露给 Nginx,它们继续在 Docker 内部网络里通信。
二、目标效果
最终用户只访问:
https://cloud.example.com:8443
并且这些功能都正常:
登录
上传
下载
文件预览
Markdown 在线编辑
SeaDoc 在线文档编辑
三、准备工作
假设 Seafile 的 compose 目录是:
/opt/seafile
目录里通常有:
.env
seafile-server.yml
caddy.yml
seadoc.yml
先备份:
cd /opt/seafile
cp .env .env.bak.$(date +%F-%H%M)
cp seafile-server.yml seafile-server.yml.bak.$(date +%F-%H%M)
cp seadoc.yml seadoc.yml.bak.$(date +%F-%H%M)
四、修改 .env
编辑:
nano /opt/seafile/.env
找到类似内容:
COMPOSE_FILE='seafile-server.yml,caddy.yml,seadoc.yml'
SEAFILE_SERVER_HOSTNAME=192.168.1.20
SEAFILE_SERVER_PROTOCOL=http
JWT_PRIVATE_KEY=
改成:
COMPOSE_FILE='seafile-server.yml,seadoc.yml'
SEAFILE_SERVER_HOSTNAME=cloud.example.com:8443
SEAFILE_SERVER_PROTOCOL=https
JWT_PRIVATE_KEY=这里填写随机字符串
SEADOC_SERVER_URL=https://cloud.example.com:8443/sdoc-server
SEAFILE_SERVICE_URL=https://cloud.example.com:8443
生成随机字符串:
openssl rand -hex 32
然后填到:
JWT_PRIVATE_KEY=生成出来的随机字符串
注意:
SEAFILE_SERVER_HOSTNAME 要写公网访问域名和端口
SEAFILE_SERVER_PROTOCOL 要写 https
JWT_PRIVATE_KEY 不要留空
如果公网访问不用特殊端口,比如直接是 https://cloud.example.com,那就写:
SEAFILE_SERVER_HOSTNAME=cloud.example.com
SEAFILE_SERVER_PROTOCOL=https
SEADOC_SERVER_URL=https://cloud.example.com/sdoc-server
SEAFILE_SERVICE_URL=https://cloud.example.com
五、修改 seafile-server.yml
编辑:
nano /opt/seafile/seafile-server.yml
找到 seafile 服务里的端口配置。
原来可能是注释状态:
# ports:
# - "80:80"
改成:
ports:
- "80:80"
然后删除或注释掉 Caddy labels:
labels:
caddy: ${SEAFILE_SERVER_PROTOCOL:-http}://${SEAFILE_SERVER_HOSTNAME:?Variable is not set or empty}
caddy.reverse_proxy: "{{upstreams 80}}"
删除后,Seafile 主服务会直接暴露到宿主机:
http://192.168.1.20:80
六、修改 seadoc.yml
编辑:
nano /opt/seafile/seadoc.yml
找到端口配置,原来可能是:
# ports:
# - "80:80"
改成:
ports:
- "8888:80"
找到:
- SEAHUB_SERVICE_URL=${SEAFILE_SERVICE_URL:-http://seafile}
改成:
- SEAHUB_SERVICE_URL=${SEAFILE_SERVICE_URL:-https://cloud.example.com:8443}
然后删除或注释掉 seadoc 服务里的 Caddy labels:
labels:
caddy: ${SEAFILE_SERVER_PROTOCOL:-http}://${SEAFILE_SERVER_HOSTNAME:?Variable is not set or empty}
caddy.1_handle_path: "/socket.io/*"
caddy.1_handle_path.0_rewrite: "* /socket.io{uri}"
caddy.1_handle_path.1_reverse_proxy: "{{upstreams 80}}"
caddy.2_handle_path: "/sdoc-server/*"
caddy.2_handle_path.0_rewrite: "* {uri}"
caddy.2_handle_path.1_reverse_proxy: "{{upstreams 80}}"
修改后,SeaDoc 会暴露到宿主机:
http://192.168.1.20:8888
七、停止 Caddy 并启动服务
因为 .env 里已经去掉了 caddy.yml:
COMPOSE_FILE='seafile-server.yml,seadoc.yml'
所以后续不会再启动 Caddy。
如果旧 Caddy 容器还在运行,停止并删除:
docker stop seafile-caddy
docker rm seafile-caddy
重新启动:
cd /opt/seafile
docker compose down
docker compose up -d
查看状态:
docker compose ps
期望看到:
seafile 0.0.0.0:80->80/tcp
seadoc 0.0.0.0:8888->80/tcp
seafile-mysql
seafile-redis
不应该再看到:
seafile-caddy
八、测试 Seafile 服务器端口
在 Nginx / Nginx Proxy Manager 那台机器上测试:
curl -I http://192.168.1.20:80
curl -I http://192.168.1.20:8888
只要有 HTTP 响应即可,不一定必须是 200。
如果访问不到,在 Seafile 服务器上检查:
docker compose ps
ss -lntp | grep -E ':80|:8888'
九、配置 Nginx Proxy Manager
如果你使用 Nginx Proxy Manager,创建或编辑 Proxy Host。
基础配置:
Domain Names:
cloud.example.com
Scheme:
http
Forward Hostname / IP:
192.168.1.20
Forward Port:
80
Websockets Support:
开启
Block Common Exploits:
可以开启
SSL 配置:
SSL Certificate:
选择你的证书
Force SSL:
开启
HTTP/2 Support:
可开可不开,排错阶段可以先关
Advanced 填入:
location /sdoc-server/ {
proxy_pass http://192.168.1.20:8888/;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Port 8443;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 100m;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
location /socket.io {
proxy_pass http://192.168.1.20:8888;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Port 8443;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
location / {
proxy_pass http://192.168.1.20:80;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Port 8443;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 0;
proxy_request_buffering off;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
如果你的公网访问没有特殊端口,是标准 443,可以把:
proxy_set_header X-Forwarded-Port 8443;
改成:
proxy_set_header X-Forwarded-Port 443;
或者删除这一行。
十、普通 Nginx 配置参考
如果你不用 Nginx Proxy Manager,而是手写 Nginx,可以参考:
server {
listen 443 ssl;
server_name cloud.example.com;
ssl_certificate /path/to/fullchain.pem;
ssl_certificate_key /path/to/privkey.pem;
client_max_body_size 0;
location /sdoc-server/ {
proxy_pass http://192.168.1.20:8888/;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Port 443;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
location /socket.io {
proxy_pass http://192.168.1.20:8888;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Port 443;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
location / {
proxy_pass http://192.168.1.20:80;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Port 443;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_request_buffering off;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
}
十一、配置 seahub_settings.py
编辑:
nano /opt/seafile-data/seafile/conf/seahub_settings.py
加入或整理成类似下面这样:
# -*- coding: utf-8 -*-
TIME_ZONE = 'Asia/Shanghai'
ENABLE_SETTINGS_VIA_WEB = False
SERVICE_URL = 'https://cloud.example.com:8443'
FILE_SERVER_ROOT = 'https://cloud.example.com:8443/seafhttp'
USE_X_FORWARDED_HOST = True
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
ALLOWED_HOSTS = [
'cloud.example.com',
'192.168.1.20',
'127.0.0.1',
'localhost',
]
CSRF_TRUSTED_ORIGINS = [
'https://cloud.example.com:8443',
]
ENABLE_SEADOC = True
SEADOC_SERVER_URL = 'https://cloud.example.com:8443/sdoc-server'
如果你是标准 HTTPS 443 端口,就改成:
SERVICE_URL = 'https://cloud.example.com'
FILE_SERVER_ROOT = 'https://cloud.example.com/seafhttp'
CSRF_TRUSTED_ORIGINS = ['https://cloud.example.com']
SEADOC_SERVER_URL = 'https://cloud.example.com/sdoc-server'
注意:
SERVICE_URL 不要写内网 IP
FILE_SERVER_ROOT 不要写内网 IP
不要在结尾多写 /
推荐:
FILE_SERVER_ROOT = 'https://cloud.example.com:8443/seafhttp'
不推荐:
FILE_SERVER_ROOT = 'http://192.168.1.20/seafhttp/'
修改后重启:
cd /opt/seafile
docker compose restart seafile seadoc
十二、不要单独代理 /seafhttp 到 8082
很多人会看到:
[fileserver]
port=8082
于是尝试把:
/seafhttp
直接反代到:
192.168.1.20:8082
这通常是错误的。
Seafile 13 Docker 版中,/seafhttp 应该先进入 Seafile 容器内置的 OpenResty/Nginx,再由它处理认证和转发。
如果直接代理到 fileserver,常见错误是:
Both token and cookie are not set
正确做法是:
/seafhttp 也走主服务 192.168.1.20:80
也就是让它进入 NPM/Nginx 的:
location /
十三、测试
重新打开浏览器,建议使用隐私窗口或清除站点 Cookie。
访问:
https://cloud.example.com:8443
依次测试:
1. 登录
2. 新建资料库
3. 上传小文件
4. 下载小文件
5. 上传 Markdown 文件
6. 在线打开 Markdown
7. 编辑并保存 Markdown
8. 上传 docx 文件
9. 测试 SeaDoc 在线编辑
下载链接应该类似:
https://cloud.example.com:8443/seafhttp/repos/xxx/files/xxx?op=download
如果下载链接变成下面这样,就是配置错了:
http://192.168.1.20/...
http://cloud.example.com/...
https://cloud.example.com/...
最后一种如果你实际公网必须带 8443,那么少了 :8443 也是错的。
十四、常见问题排查
1. 下载 400 Bad Request
先看浏览器 F12 → Network。
如果失败的是:
/seafhttp/...
检查:
FILE_SERVER_ROOT 是否是公网完整地址
Nginx 是否把 Host 转成了公网 Host
是否错误地把 /seafhttp 单独代理到了 8082
正确:
FILE_SERVER_ROOT = 'https://cloud.example.com:8443/seafhttp'
错误:
FILE_SERVER_ROOT = 'http://192.168.1.20/seafhttp'
2. Markdown 打开一直转圈
Markdown 在线编辑需要读取文件内容,底层也会请求:
/seafhttp/...op=download
所以如果下载 400,Markdown 编辑通常也会一直转圈。
先修下载,Markdown 往往会一起恢复。
3. 日志里出现 Invalid HTTP_HOST header
如果 seahub.log 里看到:
Invalid HTTP_HOST header: '127.0.0.1:8000'
就在 seahub_settings.py 里加入:
ALLOWED_HOSTS = [
'cloud.example.com',
'192.168.1.20',
'127.0.0.1',
'localhost',
]
然后重启:
docker compose restart seafile
4. 在线文档编辑打不开
重点检查:
SeaDoc 容器是否运行
192.168.1.20:8888 是否能访问
/sdoc-server/ 是否代理到 8888
/socket.io 是否代理到 8888
WebSocket 是否开启
JWT_PRIVATE_KEY 是否非空
检查容器:
docker compose ps
检查端口:
curl -I http://192.168.1.20:8888
5. 端口 80 被占用
检查:
ss -lntp | grep ':80'
如果是旧的 Caddy 占用:
docker stop seafile-caddy
docker rm seafile-caddy
如果必须换端口,比如让 Seafile 暴露到 8080:
ports:
- "8080:80"
那 Nginx/NPM 也要改成代理:
http://192.168.1.20:8080
十五、最终检查清单
.env:
COMPOSE_FILE='seafile-server.yml,seadoc.yml'
SEAFILE_SERVER_HOSTNAME=cloud.example.com:8443
SEAFILE_SERVER_PROTOCOL=https
SEADOC_SERVER_URL=https://cloud.example.com:8443/sdoc-server
SEAFILE_SERVICE_URL=https://cloud.example.com:8443
JWT_PRIVATE_KEY=非空随机字符串
seafile-server.yml:
ports:
- "80:80"
seadoc.yml:
ports:
- "8888:80"
Nginx/NPM:
/ -> 192.168.1.20:80
/sdoc-server/ -> 192.168.1.20:8888
/socket.io -> 192.168.1.20:8888
seahub_settings.py:
SERVICE_URL = 'https://cloud.example.com:8443'
FILE_SERVER_ROOT = 'https://cloud.example.com:8443/seafhttp'
CSRF_TRUSTED_ORIGINS = ['https://cloud.example.com:8443']
ALLOWED_HOSTS 包含公网域名、127.0.0.1、localhost
不要做:
不要用 sub_filter 硬替换页面内容
不要把 Host 固定成内网 IP
不要把 SERVICE_URL 写成内网 IP
不要把 FILE_SERVER_ROOT 写成内网 IP
不要单独把 /seafhttp 代理到 8082
十六、回滚
如果改乱了,恢复备份:
cd /opt/seafile
cp .env.bak.xxxx .env
cp seafile-server.yml.bak.xxxx seafile-server.yml
cp seadoc.yml.bak.xxxx seadoc.yml
docker compose down
docker compose up -d
如果要恢复官方 Caddy:
COMPOSE_FILE='seafile-server.yml,caddy.yml,seadoc.yml'
然后恢复 Caddy labels,再启动:
docker compose up -d