在kylin v10上安装seafile13的记录

因需要部署生产服务器,所以我是先在个人虚拟机上验证,再将相关操作重新在生产服务器上操作,因此多了一个本地安装操作系统与上传服务器文件和加载镜像的步骤。部署结果,在个人虚拟机上按以上步骤,在kylin v10 sp2上成功部署seafile13,并替换80端口为8099,解决头像问题。但是在服务器上部署失败,确认是由于cpu过旧不支持X86_V2指令集,引发Seafile 事件系统(seafevents)加载 NumPy 时崩溃,seafile服务无法正常启动,因此最终放弃在服务器环境部署seafile13,但在我本机vm虚拟机部署正常。经测试seafile12与seafile11版本均有numpy问题,无法在服务器上正常服务。改部署seafile10版本后可正常服务。

1. 环境说明

(1) 操作系统

麒麟v10 sp2系统

下载地址:

https://dn710201.ca.archive.org/0/items/kylin-server-10-sp-2-x-86-release-build-09-20210524/Kylin-Server-10-SP2-x86-Release-Build09-20210524.iso

安装后确认版本信息

[root@localhost ~]# uname -a

Linux localhost.localdomain 4.19.90-24.4.v2101.ky10.x86_64 #1 SMP Mon May 24 12:14:55 CST 2021 x86_64 x86_64 x86_64 GNU/Linux
[root@localhost ~]# cat /etc/os-release

NAME=“Kylin Linux Advanced Server”

VERSION=“V10 (Sword)”

ID=“kylin”

VERSION_ID=“V10”

PRETTY_NAME=“Kylin Linux Advanced Server V10 (Sword)”

ANSI_COLOR=“0;31”

(2) 关闭selinux,关闭防火墙

2. 相关软件包准备

(1) docker安装

Kylin自己的docker太低,因此安装的是26.1.4版本

网上下载docker-26.1.4.tgz,并上传到服务器安装

下载地址:https://download.docker.com/linux/static/stable/x86_64/docker-26.1.4.tgz

执行以下命令完成docker安装

在当前目录直接下载安装包:

wget https://download.docker.com/linux/static/stable/x86_64/docker-26.1.4.tgz

解压安装包:

sudo tar -xvf docker-26.1.4.tgz

移动到/usr/bin目录下:

sudo mv docker/* /usr/bin/

创建/etc/systemd/system/docker.service文件:

[Unit]

Description=Docker Application Container Engine

Documentation=https://docs.docker.com

After=network-online.target firewalld.service

Wants=network-online.target

[Service]

Type=notify

ExecStart=/usr/bin/dockerd

ExecReload=/bin/kill -s HUP $MAINPID

LimitNOFILE=infinity

LimitNPROC=infinity

LimitCORE=infinity

TimeoutStartSec=0

Delegate=yes

KillMode=process

Restart=on-failure

StartLimitBurst=3

StartLimitInterval=60s

[Install]

WantedBy=multi-user.target

开机自动启动docker服务:

sudo systemctl daemon-reload

sudo systemctl enable docker

sudo systemctl start docker

验证docker服务是否正常:

systemctl status docker

docker ps -a

docker run hello-world

(2) docker-compose安装

Kylin自己的docker-compose版本过低,安装v2.20.2版本,个人虚拟机上直接下载:

sudo curl -L https://github.com/docker/compose/releases/download/v2.20.2/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose

如果是服务器,则直接上传已下载的文件到指定位置:

sudo scp /usr/local/bin/docker-compose 服务器用户名@服务器IP:/home/seafile/uploads/

sudo mv /home/seafile/uploads/docker-compose /usr/local/bin/docker-compose

sudo chmod +x /usr/local/bin/docker-compose

验证docker-compose是否安装成功,执行以下命令显示Docker Compose version v2.20.2则为成功

sudo docker-compose version

3. 安装seafile13

(1) seafile用户

创建seafile用户:

sudo useradd -m -s /bin/bash seafile

设置密码:

sudo passwd seafile

加入到docker组(因为需要使用seafile用户执行docker与docker-compose):

usermod -aG docker seafile

设置sudo免密

echo “seafile ALL=(ALL) NOPASSWD:ALL” | sudo tee /etc/sudoers.d/seafile

chmod 440 /etc/sudoers.d/seafile

重新登录 seafile 用户,退出当前终端,再登录:

su - seafile

验证 docker 权限是否生效:

docker ps

验证免密是否生效:

sudo ls /

(2) 创建/opt/seafile目录

创建目录:

mkdir -p /opt/seafile

赋予 seafile 用户权限

chown -R seafile:seafile /opt/seafile

(3) 创建/opt/seafile目录和data目录

/opt/seafile目录用来放yml文件和env文件
/opt/seafile/data目录用来保存seafile相关数据

(4) 下载yml文件和env文件

wget -O .env https://manual.seafile.com/13.0/repo/docker/ce/env

wget https://manual.seafile.com/13.0/repo/docker/ce/seafile-server.yml

wget https://manual.seafile.com/13.0/repo/docker/seadoc.yml

wget https://manual.seafile.com/13.0/repo/docker/caddy.yml

(5) 修改env文件

更改Images部分,修改docker镜像版本为kylin v10适用版本

SEAFILE_DB_IMAGE=mariadb:10.5

SEAFILE_REDIS_IMAGE=redis:5.0

更改Persistent Storage持久化数据文件位置

BASIC_STORAGE_PATH=./data

更改Startup parameters启动参数

SEAFILE_SERVER_HOSTNAME=你的IP:端口

SEAFILE_SERVER_PROTOCOL=http

TIME_ZONE=Asia/Shanghai

JWT_PRIVATE_KEY=自己生成的key

更改Database数据库参数,这里用户名是seafile,密码是Seafile0228

SEAFILE_MYSQL_DB_USER=seafile

SEAFILE_MYSQL_DB_PASSWORD=Seafile0228

更改数据库root用户初始密码

INIT_SEAFILE_MYSQL_ROOT_PASSWORD=Seafile0228

更改seafile平台的管理员初始账号与密码

INIT_SEAFILE_ADMIN_EMAIL=seafile@126.com

INIT_SEAFILE_ADMIN_PASSWORD=Seafile0228

(6) 修改seafile-server.yml 内容

在每个服务里,在 restart: unless-stopped这行下面增加以下内容:

security_opt:

  - seccomp=unconfined

作用:关闭 Docker 的 seccomp 沙箱,让容器可以使用所有系统调用(syscall),不再被限制。

在seafile服务volumes标签下增加setup-seafile-mysql.sh文件映射

- /opt/seafile-patch/setup-seafile-mysql.sh:/opt/seafile/seafile-server-13.0.18/setup-seafile-mysql.sh

作用:解决mariadb容器在启动时找不到python3的问题

(7) 启动容器

在服务器的/opt/seafile目录下,以seafile用户执行以下命令,会直接启动容器

docker-compose up -d

(8) 验证服务是否正常

查看日志

docker logs seafile

查看网页,我虚拟机ip是192.168.119.130

http://192.168.119.130/

显示登录页代表正常启动了。

4. 集成onlyoffice.yml

(1) 下载onlyoffice.yml文件

在/opt/seafile目录下执行以下命令下载:

wget https://manual.seafile.com/13.0/repo/docker/onlyoffice.yml

(2) 修改.env文件

增加加载的yml文件

COMPOSE_FILE=‘seafile-server.yml,caddy.yml,seadoc.yml,onlyoffice.yml’

(3) 重新启动容器服务即可

停止

docker-compose down

启动

docker-compose up -d

5. Seafile平台访问端口号80变为8099

由于大部分生产环境不允许开放80端口,因此我在此改为8099端口。修改方法参照了这位网友的方法,参照地址为:https://bbs.seafile.com/t/topic/22333

seafile主服务不使用caddy,使用了nginx,不过仍然保留caddy做为seadoc的代理。

因为要用nginx代理seafile,所以这里的80端口先注释掉

(1) 修改seafile-server.yml内容

seafile服务下端口配置为:

ports:

  - "8000:8000"

  - "8080:8080"

  - "8082:8082"

Seafile服务下删除或注释掉label标签,不使用caddy

# labels:

# caddy: ${SEAFILE_SERVER_PROTOCOL:-http}://${SEAFILE_SERVER_HOSTNAME:?Variable is not set or empty}

# caddy.reverse_proxy: “{{upstreams 80}}”

(2) 修改seadoc.yml 内容

seadoc服务的端口配置为

ports:

  - 8089:80

labels标签更改为

labels:

  caddy: ${SEAFILE_SERVER_PROTOCOL:-http}://${SEAFILE_SERVER_HOSTNAME:?Variable is not set or empty}

(3) 修改caddy.yml 内容

caddy服务端口配置为

ports:

# - 80:80

- 443:443

(4) 修改onlyoffice.yml 内容

修改labbels标签内容,原SEAFILE_SERVER_HOSTNAME替换成了ONLYOFFICE_HOSTNAME。因为在.env文件里已经配置了EAFILE_SERVER_HOSTNAME为带端口号的地址,直接使用会报错,因此在.env文件配置ONLYOFFICE_HOSTNAME变量为不带端口号的地址,在这里引用此变量

labels:

  caddy: ${SEAFILE_SERVER_PROTOCOL:-http}://${ONLYOFFICE_HOSTNAME:?Variable is not set or empty}:${ONLYOFFICE_PORT:-6233}

(5) Nginx配置文件

新建文件/etc/nginx/conf.d/seafile-8099.conf,文件内容在后文贴出。

另外在/etc/nginx/nginx.conf文件中做了全局加固,在http{}块中加固,位置在

default_type application/octet-stream;

这行下面,增加以下内容:

# 全局限流区定义(必须在 http{} 里)

limit_conn_zone $binary_remote_addr zone=addr:10m;

limit_req_zone $binary_remote_addr zone=req_limit_per_ip:10m rate=100r/s;

最后注释掉该文件默认开启80端口的servers配置

(6) 验证是否成功

在/opt/seafile目录下,以seafile用户执行以下命令,会直接启动容器

docker-compose up -d

查看日志

docker logs seafile

查看网页,我虚拟机ip是192.168.119.130

http://192.168.119.130/

显示登录页代表正常启动了。

(7) 增加防火墙配置

因为是服务器正式使用时需要开启防火墙,因此需要放行端口

开启防火墙

sudo systemctl start firwalld

放行8099和6233端口

sudo firewall-cmd --zone=public --add-port=8099/tcp --permanent

sudo firewall-cmd --zone=public --add-port=6233/tcp --permanent

开启 masquerade(NAT 转发),不开启无法转发到容器端口

sudo firewall-cmd --zone=public --add-masquerade --permanent

重新加载生效配置信息

sudo firewall-cmd --reload

(8) 整理部署过程中遇到的问题如下:

问题1:seafile-mysql没有启动成功,查看seafile-mysql容器日志,发现报错信息Can’t initialize timers

原因是MariaDB 10.11 在银河麒麟 V10(国产内核)上无法初始化定时器(timer)。需将镜像换成10.5即可解决,修改docker-compose.yml文件,将image:mariadb:10.11更改为image: mariadb:10.5。

问题2:seafile-mysql启动成功,但是拒绝连接。docker logs seafile-mysql日志中最后提到[Warning] Access denied for user ‘seafile’@‘192.168.0.4’ (using password: YES)

原因是MariaDB 初始化时没有使用配置文件中提供的 root 密码,而是自动生成了一个随机密码。MariaDB 初始化日志里有这一段[Entrypoint]: Securing system users (equivalent to running mysql_secure_installation)

MariaDB 在初始化时执行了类似 mysql_secure_installation 的逻辑。这个逻辑会自动生成 root 密码(而不是使用.env里提供的密码),这就是现在无法登录 root 用户的原因,为了保险起见,root用户与seafile用户密码,都重置一下

跳过权限模式启动 MariaDB(临时模式)

docker run --rm -it \

-v /opt/seafile-mysql/db:/var/lib/mysql \

mariadb:10.5 \

mysqld --skip-grant-tables --user=mysql

打开另一个终端,进入临时 MySQL

docker exec -it $(docker ps -q --filter ancestor=mariadb:10.5) mysql -uroot

FLUSH PRIVILEGES;

ALTER USER ‘root’@‘localhost’ IDENTIFIED BY ‘你的root密码’;

ALTER USER ‘root’@‘%’ IDENTIFIED BY ‘你的root密码’;

ALTER USER ‘root’@‘localhost’ IDENTIFIED BY ‘你的seafile密码’;

ALTER USER ‘seafile’@‘%’ IDENTIFIED BY ‘你的seafile密码’;

FLUSH PRIVILEGES;

退出 MySQL,关闭临时容器,启动正式 MariaDB 容器

docker start seafile-mysql

测试 root 密码

docker exec -it seafile-mysql mysql -uroot -p

输入你刚刚设置的密码。如果成功,代表root 密码已修复

问题3:seafile容器启动失败,报错提示

[WARNING] ../common/seaf-db.c(266): Failed to create mysql connection keepalive thread.

[WARNING] ../common/seaf-utils.c(237): Failed to start mysql db.

[WARNING] seafile-session.c(243): Failed to load database config.

[WARNING] seaf-server.c(1353): Failed to create seafile session.

因上面提示有数据库问题,因此在seafile容器内测试到mysql的连接

root@d92f5b7998d9:/opt/seafile# curl seafile-mysql:3306

curl: (6) getaddrinfo() thread failed to start

原因是 glibc 在创建解析线程时失败,做了个小测试验证此问题,在容器内执行pthread_create(&t, NULL, f, NULL),返回结果ret = 1,说明容器环境本身无法创建线程,开始怀疑 seccomp / glibc / clone() 被拦截,执行以下命令测试grep Seccomp /proc/1/status,看到结果是Seccomp: 2,说明容器处于严格 seccomp 模式。然后在seafile-server.yml文件中增加以下内容

security_opt:

  • seccomp=unconfined

重启容器后测试pthread_create(&t, NULL, f, NULL),返回结果ret = 0

问题4:seadoc容器启动失败,报错提示

Assertion failed: (0) == (uv_thread_create(t.get(), start_thread, this))

Aborted (core dumped)及RuntimeError: can’t start new thread

原因与问题3相同,因此同样在seadoc.yml文件内容中增加以下内容:

security_opt:

  • seccomp=unconfined

为了保险起见,后来在其他服务里也均增加了这个内容

问题5:seafile容器启动失败,docker logs seafile日志中提示The current version of python is not 3.x.x, please use Python 3.x.x .

这可能是seafile 官方脚本在国产系统上的兼容性 bug,我在centos7.2上没有此问题。解决方法是需要显示指定python3位置。

将容器内setup-seafile-mysql.sh文件复制到宿主机上,放在data目录里是方便将来一起做数据备份

sudo mkdir /opt/seafile/data/seafile-patch/

docker cp seafile:/opt/seafile/seafile-server-13.0.18/setup-seafile-mysql.sh /opt/seafile/data/seafile-patch/

修改setup-seafile-mysql.sh文件内容,拦截后续判断,显示指定python位置。在check_python_executable()函数中增加了以下判断,增加位置在function check_python_executable() {这行代码下方,也就是函数内容的最前面。

if [ -f /usr/bin/python3 ]; then

    PYTHON=/usr/bin/python3

    return 0

fi

同时在seafile-sever.yml文件中,在seafile服务的volumes标签下增加了宿主机文件到容器文件的映射。
/opt/seafile/data/seafile-patch/setup-seafile-mysql.sh:/opt/seafile/seafile-server-13.0.18/setup-seafile-mysql.sh

问题6:默认头像不能正常显示,但是上传新头像成功后,新头像还是不能正常显示。 修改方法参照了这位网友的方法,参照地址为:https://bbs.seafile.com/t/topic/22333,并有一小差别

从容器内复制 media 目录到宿主机

docker cp seafile:/shared/seafile-data/seafile/media /opt/seafile/data/seafile-data/seafile/

修复宿主机 media 目录的软链接

rm -f /opt/seafile/data/seafile-data/seafile/media/avatars

ln -s /opt/seafile/data/seafile-data/seafile/media/avatars /opt/seafile/data/seafile-data/seafile/media/avatars
rm -f /opt/seafile/data/seafile-data/seafile/media/custom

ln -s /opt/seafile/data/seafile-data/seafile/media/custom /opt/seafile/data/seafile-data/seafile/media/custom

宿主机nginx配置修正,这里我按网友的方法没成功,改为如下配置成功显示头像
location /media/ {

alias /opt/seafile/data/seafile-data/seafile/media/;

}

问题7:Redis容器启动报错,提示Fatal: Can’t initialize Background Jobs. Error message: Operation not permitted

默认的seafile-server.yml文件里拉取的是Redis 8.x镜像,而Redis 8.x是 2024 年之后的新版本,它启用了新的后台任务线程模型,需要内核支持 clone3、io_uring、cgroup v2 等特性。银河麒麟 V10(基于 FreeBSD/DragonFly 衍生内核 + 国产补丁)不支持这些特性。

但是换redis6.2仍然有问题,日志中有以下提示

Fatal: Can’t initialize Background Jobs.

其原因是银河麒麟 V10 内核不支持 Redis 的后台任务机制(fork + background save + AOF rewrite)

换成redis5.0解决

更换方法是修改.env文件的redis镜像版本

SEAFILE_REDIS_IMAGE=redis:5.0

并重新拉取镜像

问题8:执行docker-compose -f seafile-server.yml pull直接报错ERROR: Invalid interpolation format for “db” option in service “services”: “${SEAFILE_DB_IMAGE:-mariadb:10.11}”

说明 docker‑compose 1.22.0(你当前使用的版本)不支持这种变量默认值语法:${VAR:-default}

这是 Compose v2 才支持的语法,因此重新安装v2.20.2版本

问题9:本地跑通后,在服务器部署,访问8099端口,页面提示Internal Server Error

检查seahub容器情况docker logs seahub,提示

Error response from daemon: No such container: seahub

RuntimeError: NumPy was built with baseline optimizations:

(X86_V2) but your machine doesn’t support:

(X86_V2).

Seafile 事件系统(seafevents)加载 NumPy 时崩溃:CPU 不支持 X86_V2 指令集

使用以下命令查看当前虚拟上支持的指令集与宿主机的cpu信息

cat /proc/cpuinfo | grep flags

cat /proc/cpuinfo | grep -E “model name|flags” | head -4

确认是cpu过旧不支持该指令集。因此放弃在服务器环境部署seafile13。

后面附上各个配置文件内容:

.env文件内容

#################################

# Docker compose configurations #

#################################

COMPOSE_FILE=‘seafile-server.yml,caddy.yml,seadoc.yml,onlyoffice.yml’

COMPOSE_PATH_SEPARATOR=‘,’

## Images

SEAFILE_IMAGE=seafileltd/seafile-mc:13.0-latest

SEAFILE_DB_IMAGE=mariadb:10.5

SEAFILE_REDIS_IMAGE=redis:5.0

SEAFILE_CADDY_IMAGE=lucaslorentz/caddy-docker-proxy:2.9-alpine

SEADOC_IMAGE=seafileltd/sdoc-server:2.0-latest

NOTIFICATION_SERVER_IMAGE=seafileltd/notification-server:13.0-latest

MD_IMAGE=seafileltd/seafile-md-server:13.0-latest

## Persistent Storage

BASIC_STORAGE_PATH=./data

SEAFILE_VOLUME=$BASIC_STORAGE_PATH/seafile-data

SEAFILE_MYSQL_VOLUME=$BASIC_STORAGE_PATH/seafile-mysql/db

SEAFILE_CADDY_VOLUME=$BASIC_STORAGE_PATH/seafile-caddy

SEADOC_VOLUME=$BASIC_STORAGE_PATH/seadoc-data

#################################

# Startup parameters #

#################################

SEAFILE_SERVER_HOSTNAME=192.168.119.130:8099

SEAFILE_SERVER_PROTOCOL=http

TIME_ZONE=Asia/Shanghai

JWT_PRIVATE_KEY=3f8c2e9b4a0d7f1c9e2b8d4f6a1c3e7f9b2d4c6e8a0b1c3d5e7f9a1b3c5d7e9

#####################################

# Third-party service configuration #

#####################################

## Database

SEAFILE_MYSQL_DB_HOST=db

SEAFILE_MYSQL_DB_USER=seafile

SEAFILE_MYSQL_DB_PASSWORD=Seafile0228

SEAFILE_MYSQL_DB_CCNET_DB_NAME=ccnet_db

SEAFILE_MYSQL_DB_SEAFILE_DB_NAME=seafile_db

SEAFILE_MYSQL_DB_SEAHUB_DB_NAME=seahub_db

## Cache

CACHE_PROVIDER=redis # or memcached

### Redis

REDIS_HOST=redis

REDIS_PORT=6379

REDIS_PASSWORD=

### Memcached

MEMCACHED_HOST=memcached

MEMCACHED_PORT=11211

######################################

# Initial variables #

# (Only valid in first-time startup) #

######################################

## Database root password, Used to create Seafile users

INIT_SEAFILE_MYSQL_ROOT_PASSWORD=Seafile0228

## Seafile admin user

INIT_SEAFILE_ADMIN_EMAIL=seafile@126.com

INIT_SEAFILE_ADMIN_PASSWORD=Seafile0228

############################################

# Additional configurations for extensions #

############################################

## SeaDoc service

ENABLE_SEADOC=true

## Notification

ENABLE_NOTIFICATION_SERVER=false

NOTIFICATION_SERVER_URL=

## Seafile AI

ENABLE_SEAFILE_AI=false

ENABLE_FACE_RECOGNITION=false

SEAFILE_AI_LLM_TYPE=openai

SEAFILE_AI_LLM_URL=

SEAFILE_AI_LLM_KEY= # your llm key

SEAFILE_AI_LLM_MODEL=gpt-4o-mini

## Metadata server

MD_FILE_COUNT_LIMIT=100000

## add onlyoffice

# OnlyOffice 镜像

ONLYOFFICE_IMAGE=onlyoffice/documentserver:8.1.0.1

# OnlyOffice 地址

ONLYOFFICE_HOSTNAME=192.168.119.130

# OnlyOffice 的持久化存储目录

ONLYOFFICE_VOLUME=/opt/seafile/data/onlyoffice

# OnlyOffice 文档服务器端口

ONLYOFFICE_PORT=6233

# jwt 密钥,通过 `pwgen -s 40 1` 生成

ONLYOFFICE_JWT_SECRET=Q9tFz3WmA8rLk2VhS7pXc4JbN1yR0eTgH5uPqZs

seafile-sever.yml内容
services:

db:

image: ${SEAFILE_DB_IMAGE:-mariadb:10.11}

container_name: seafile-mysql

restart: unless-stopped

security_opt:

  - seccomp=unconfined

environment:

  - MYSQL_ROOT_PASSWORD=${INIT_SEAFILE_MYSQL_ROOT_PASSWORD:-}

  - MYSQL_LOG_CONSOLE=true

  - MARIADB_AUTO_UPGRADE=1

volumes:

  - "${SEAFILE_MYSQL_VOLUME:-/opt/seafile-mysql/db}:/var/lib/mysql"

networks:

  - seafile-net

healthcheck:

  test:

    \[

      "CMD",

      "/usr/local/bin/healthcheck.sh",

      "--connect",

      "--mariadbupgrade",

      "--innodb_initialized",

    \]

  interval: 20s

  start_period: 30s

  timeout: 5s

  retries: 10

redis:

image: ${SEAFILE_REDIS_IMAGE:-redis}

container_name: seafile-redis

restart: unless-stopped

security_opt:

  - seccomp=unconfined

command:

  - /bin/sh

  - -c

  - redis-server --requirepass "$$REDIS_PASSWORD"

environment:

  - REDIS_PASSWORD=${REDIS_PASSWORD:-}

networks:

  - seafile-net

seafile:

image: ${SEAFILE_IMAGE:-seafileltd/seafile-mc:13.0-latest}

container_name: seafile

restart: unless-stopped

ports:

  - "8000:8000"

  - "8080:8080"

  - "8082:8082"

security_opt:

  - seccomp=unconfined

  - apparmor=unconfined

volumes:

  - ${SEAFILE_VOLUME:-/opt/seafile-data}:/shared

  - /opt/seafile/data/seafile-patch/setup-seafile-mysql.sh:/opt/seafile/seafile-server-13.0.18/setup-seafile-mysql.sh

environment:

  - SEAFILE_MYSQL_DB_HOST=${SEAFILE_MYSQL_DB_HOST:-db}

  - SEAFILE_MYSQL_DB_PORT=${SEAFILE_MYSQL_DB_PORT:-3306}

  - SEAFILE_MYSQL_DB_USER=${SEAFILE_MYSQL_DB_USER:-seafile}

  - SEAFILE_MYSQL_DB_PASSWORD=${SEAFILE_MYSQL_DB_PASSWORD:?Variable is not set or empty}

  - INIT_SEAFILE_MYSQL_ROOT_PASSWORD=${INIT_SEAFILE_MYSQL_ROOT_PASSWORD:-}

  - 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=${TIME_ZONE:-Etc/UTC}

  - INIT_SEAFILE_ADMIN_EMAIL=${INIT_SEAFILE_ADMIN_EMAIL:-me@example.com}

  - INIT_SEAFILE_ADMIN_PASSWORD=${INIT_SEAFILE_ADMIN_PASSWORD:-asecret}

  - SEAFILE_SERVER_HOSTNAME=${SEAFILE_SERVER_HOSTNAME:?Variable is not set or empty}

  - SEAFILE_SERVER_PROTOCOL=${SEAFILE_SERVER_PROTOCOL:-http}

  - SITE_ROOT=${SITE_ROOT:-/}

  - NON_ROOT=${NON_ROOT:-false}

  - JWT_PRIVATE_KEY=${JWT_PRIVATE_KEY:?Variable is not set or empty}

  - SEAFILE_LOG_TO_STDOUT=${SEAFILE_LOG_TO_STDOUT:-false}

  - ENABLE_GO_FILESERVER=${ENABLE_GO_FILESERVER:-true}

  - ENABLE_SEADOC=${ENABLE_SEADOC:-true}

  - SEADOC_SERVER_URL=${SEAFILE_SERVER_PROTOCOL:-http}://${SEAFILE_SERVER_HOSTNAME:?Variable is not set or empty}/sdoc-server

  - CACHE_PROVIDER=${CACHE_PROVIDER:-redis}

  - REDIS_HOST=${REDIS_HOST:-redis}

  - REDIS_PORT=${REDIS_PORT:-6379}

  - REDIS_PASSWORD=${REDIS_PASSWORD:-}

  - MEMCACHED_HOST=${MEMCACHED_HOST:-memcached}

  - MEMCACHED_PORT=${MEMCACHED_PORT:-11211}

  - ENABLE_NOTIFICATION_SERVER=${ENABLE_NOTIFICATION_SERVER:-false}

  - INNER_NOTIFICATION_SERVER_URL=${INNER_NOTIFICATION_SERVER_URL:-http://notification-server:8083}

  - NOTIFICATION_SERVER_URL=${NOTIFICATION_SERVER_URL:-${SEAFILE_SERVER_PROTOCOL:-http}://${SEAFILE_SERVER_HOSTNAME:?Variable is not set or empty}/notification}

  - ENABLE_SEAFILE_AI=${ENABLE_SEAFILE_AI:-false}

  - ENABLE_FACE_RECOGNITION=${ENABLE_FACE_RECOGNITION:-false}

  - SEAFILE_AI_SERVER_URL=${SEAFILE_AI_SERVER_URL:-http://seafile-ai:8888}

  - SEAFILE_AI_SECRET_KEY=${JWT_PRIVATE_KEY:?Variable is not set or empty}

  - MD_FILE_COUNT_LIMIT=${MD_FILE_COUNT_LIMIT:-100000}

# labels:

# caddy: ${SEAFILE_SERVER_PROTOCOL:-http}://${SEAFILE_SERVER_HOSTNAME:?Variable is not set or empty}

# caddy.reverse_proxy: “{{upstreams 80}}”

healthcheck:

  test: \["CMD-SHELL", "curl -f http://localhost:80 || exit 1"\]

  interval: 30s

  timeout: 10s

  retries: 3

  start_period: 10s

depends_on:

  db:

    condition: service_healthy

  redis:

    condition: service_started

networks:

  - seafile-net

networks:

seafile-net:

name: seafile-net

caddy.yml内容

services:

caddy:

image: ${SEAFILE_CADDY_IMAGE:-lucaslorentz/caddy-docker-proxy:2.9-alpine}

restart: unless-stopped

container_name: seafile-caddy

security_opt:

  - seccomp=unconfined

ports:

# - 80:80

  - 443:443

environment:

  - CADDY_INGRESS_NETWORKS=seafile-net

volumes:

  - /var/run/docker.sock:/var/run/docker.sock

  - ${SEAFILE_CADDY_VOLUME:-/opt/seafile-caddy}:/data/caddy

networks:

  - seafile-net

healthcheck:

  test: \["CMD-SHELL", "curl --fail http://localhost:2019/metrics || exit 1"\]

  start_period: 20s

  interval: 20s

  timeout: 5s

  retries: 3

networks:

seafile-net:

name: seafile-net

seadoc.yml内容

services:

seadoc:

image: ${SEADOC_IMAGE:-seafileltd/sdoc-server:2.0-latest}

container_name: seadoc

restart: unless-stopped

security_opt:

  - seccomp=unconfined

volumes:

  - ${SEADOC_VOLUME:-/opt/seadoc-data/}:/shared

ports:

  - 8089:80

environment:

  - DB_HOST=${SEAFILE_MYSQL_DB_HOST:-db}

  - DB_PORT=${SEAFILE_MYSQL_DB_PORT:-3306}

  - DB_USER=${SEAFILE_MYSQL_DB_USER:-seafile}

  - DB_PASSWORD=${SEAFILE_MYSQL_DB_PASSWORD:?Variable is not set or empty}

  - DB_NAME=${SEADOC_MYSQL_DB_NAME:-${SEAFILE_MYSQL_DB_SEAHUB_DB_NAME:-seahub_db}}

  - TIME_ZONE=${TIME_ZONE:-Etc/UTC}

  - JWT_PRIVATE_KEY=${JWT_PRIVATE_KEY:?Variable is not set or empty}

  - NON_ROOT=${NON_ROOT:-false}

  - SEAHUB_SERVICE_URL=${SEAFILE_SERVICE_URL:-http://seafile}

labels:

  caddy: ${SEAFILE_SERVER_PROTOCOL:-http}://${SEAFILE_SERVER_HOSTNAME:?Variable is not set or empty}

  caddy.@ws.0_header: "Connection \*Upgrade\*"

  caddy.@ws.1_header: "Upgrade websocket"

  caddy.0_reverse_proxy: "@ws {{upstreams 80}}"

  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}}"

depends_on:

  db:

    condition: service_healthy

networks:

  - seafile-net

networks:

seafile-net:

name: seafile-net

onlyoffic.yml内容

services:

caddy:

ports:

  - ${ONLYOFFICE_PORT:-6233}:${ONLYOFFICE_PORT:-6233}

onlyoffice:

image: ${ONLYOFFICE_IMAGE:-onlyoffice/documentserver:8.1.0.1}

restart: unless-stopped

security_opt:

  - seccomp=unconfined

container_name: seafile-onlyoffice

environment:

  #- DB_TYPE=${DB_TYPE:-mariadb}

  #- DB_HOST=${SEAFILE_MYSQL_DB_HOST:-db}

  #- DB_USER=${SEAFILE_MYSQL_DB_USER:-seafile}

  #- DB_PWD=${SEAFILE_MYSQL_DB_PASSWORD:?Variable is not set or empty}

  - JWT_ENABLED=true

  - JWT_SECRET=${ONLYOFFICE_JWT_SECRET:?Variable is not set or empty}

volumes:

  - ${ONLYOFFICE_VOLUME:-/opt/onlyoffice}/logs:/var/log/onlyoffice

  - ${ONLYOFFICE_VOLUME:-/opt/onlyoffice}/data:/var/www/onlyoffice/Data

  - ${ONLYOFFICE_VOLUME:-/opt/onlyoffice}/lib:/var/lib/onlyoffice

labels:

  caddy: ${SEAFILE_SERVER_PROTOCOL:-http}://${ONLYOFFICE_HOSTNAME:?Variable is not set or empty}:${ONLYOFFICE_PORT:-6233}

  caddy.reverse_proxy: "{{upstreams}}"

networks:

  - seafile-net

networks:

seafile-net:

name: seafile-net

nginx文件内容:
server {

listen 8099;



\# -------------------------

\# 全局安全加固(server 级)

\# -------------------------

server_tokens off;



add_header X-Frame-Options "SAMEORIGIN" always;

add_header X-XSS-Protection "1; mode=block" always;

add_header X-Content-Type-Options "nosniff" always;

add_header Referrer-Policy "strict-origin-when-cross-origin" always;

add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;



\# 限制请求方法(不影响 Seafile / OnlyOffice / Seadoc)

if ($request_method !\~ ^(GET|POST|HEAD|OPTIONS)$) {

    return 405;

}



\# 简单防扫描(正常浏览器不会命中)

if ($http_user_agent \~\* (sqlmap|nmap|nikto|fimap|nessus|acunetix)) {

    return 403;

}



\# 如需限制 Host,可按需打开并改成你的域名/IP

\# if ($host !\~\* ^(yourdomain\\.com|你的IP)$) {

\#     return 403;

\# }



\# 使用在 http{} 里定义好的 zone

limit_conn addr 20;



\# 禁止访问敏感隐藏文件

location \~ /\\.(git|svn|hg|env|htaccess|bash|ssh) {

    deny all;

}



\# -------------------------

\# Seafile 主站点(8000)

\# -------------------------

location / {

    \# 每 IP 限制请求速率(使用 http{} 里定义的 zone)

    limit_req zone=req_limit_per_ip burst=20 nodelay;



    proxy_pass http://127.0.0.1:8000/;

    proxy_read_timeout 1200s;

    proxy_set_header Host $http_host;

    proxy_set_header Forwarded "for=$remote_addr;proto=$scheme";

    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 Connection "";

    proxy_http_version 1.1;

    client_max_body_size 0;

}



\# -------------------------

\# Seafile 文件上传(8082)

\# -------------------------

location /seafhttp {

    rewrite ^/seafhttp(.\*)$ $1 break;

    proxy_pass http://127.0.0.1:8082;

    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    client_max_body_size 0;

    proxy_connect_timeout 36000s;

    proxy_read_timeout 36000s;

    proxy_request_buffering off;

}



\# -------------------------

\# WebDAV(8080)

\# -------------------------

location /seafdav {

    proxy_pass http://127.0.0.1:8080/seafdav;

    proxy_set_header Host $host;

    proxy_set_header X-Real-IP $remote_addr;

    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    proxy_set_header X-Forwarded-Host $server_name;

    proxy_set_header X-Forwarded-Proto $scheme;

    proxy_read_timeout 1200s;

    client_max_body_size 0;

}



\# -------------------------

\# 静态资源(头像、CSS、JS)

\# -------------------------

location /media/ {

    alias /opt/seafile/data/seafile-data/seafile/media/;

}



\# -------------------------

\# Seadoc(8089)

\# -------------------------

location /sdoc-server/ {

    add_header Access-Control-Allow-Origin \*;

    add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;

    add_header Access-Control-Allow-Headers "deviceType,token, authorization, content-type";



    if ($request_method = 'OPTIONS') {

        return 204;

    }



    proxy_pass http://127.0.0.1:8089/;

    proxy_redirect off;

    proxy_set_header Host $host;

    proxy_set_header X-Real-IP $remote_addr;

    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    proxy_set_header X-Forwarded-Host $server_name;

    proxy_set_header X-Forwarded-Proto $scheme;

    client_max_body_size 100m;

}



\# -------------------------

\# Seadoc WebSocket

\# -------------------------

location /socket.io {

    proxy_pass http://127.0.0.1:8089;

    proxy_http_version 1.1;

    proxy_set_header Upgrade $http_upgrade;

    proxy_set_header Connection 'upgrade';

}

}