2026-02-06 13:29:36
La Suite Drive是法国政府主导的又一个开源网盘程序。特点如下:
为什么把易于安装给划掉?因为这个项目我认为就目前而言是非常难以安装的,官方没有提供生产部署的文档,且部署步骤太多太杂了。。我认为这与官方的宣传不符,所以给划掉了= =
这里我为了尝鲜体验一番,记录一下用docker compose部署的方式。如果后续官方写了部署文档,请以官方的文档为准。这个项目目前应该还是Beta状态,如果官方后续对项目做了比较大的改动,不保证这篇文章的部署方法一直有效。
准备工作:
1.一台Debian服务器,内存不低于4GB,放行80、443、9199、9299、9980端口。
2.添加域名A记录解析,本文示例域名:drive.example.com、onlyoffice.example.com、collabora.example.com。
3.部署RustFS对象存储服务,或Garage S3对象存储服务。推荐RustFS,因为Drive的文档编辑功能需要S3支持“版本控制”的功能,Garage S3缺少这个功能将导致文档无法保存。本文S3示例域名:rustfs.example.com
4.部署VoidAuth OIDC身份验证服务。本文示例域名:voidauth.example.com
在VoidAuth创建OIDC APP:
设置Redirect URLs:
https://drive.example.com/api/v1.0/callback/
设置PostLogout URL:
https://drive.example.com/api/v1.0/logout-callback/
在RustFS控制台创建存储桶,并启用版本控制:
做好上面的准备工作后,现在可以正式部署Drive了,在服务器内安装Docker:
apt -y update apt -y install curl curl -fsSL https://get.docker.com -o get-docker.sh sh get-docker.sh
新建compose文件:
mkdir -p /opt/drive && cd /opt/drive && nano docker-compose.yml
写入如下内容,需要修改的地方写了注释:
x-common-env: &common-env BACKEND_HOST: backend FRONTEND_HOST: frontend # Django DJANGO_ALLOWED_HOSTS: "*" DJANGO_SECRET_KEY: 7cafccee9288097a2b3eb64305b0d07bff6682bc50a2b3f99a9696ef2e000fab # 使用openssl rand -hex 32生成 DJANGO_SETTINGS_MODULE: drive.settings DJANGO_CONFIGURATION: Production # Logging LOGGING_LEVEL_HANDLERS_CONSOLE: ERROR LOGGING_LEVEL_LOGGERS_ROOT: INFO LOGGING_LEVEL_LOGGERS_APP: INFO # Python PYTHONPATH: /app # Mail DJANGO_EMAIL_HOST: mail.example.com DJANGO_EMAIL_HOST_USER: smtp DJANGO_EMAIL_HOST_PASSWORD: smtppassword DJANGO_EMAIL_PORT: 587 DJANGO_EMAIL_FROM: [email protected] DJANGO_EMAIL_USE_TLS: true DJANGO_EMAIL_BRAND_NAME: "La Suite Numérique" DJANGO_EMAIL_LOGO_IMG: "https://drive.example.com/assets/logo-suite-numerique.png" # Media S3 STORAGES_STATICFILES_BACKEND: django.contrib.staticfiles.storage.StaticFilesStorage AWS_S3_ACCESS_KEY_ID: imlala AWS_S3_SECRET_ACCESS_KEY: s3secretaccesskey AWS_S3_REGION_NAME: auto AWS_STORAGE_BUCKET_NAME: drive-media-storage AWS_S3_SIGNATURE_VERSION: s3v4 AWS_S3_ENDPOINT_URL: https://rustfs.example.com MEDIA_BASE_URL: https://drive.example.com # OIDC OIDC_OP_JWKS_ENDPOINT: https://voidauth.example.com/oidc/jwks OIDC_OP_AUTHORIZATION_ENDPOINT: https://voidauth.example.com/oidc/auth OIDC_OP_TOKEN_ENDPOINT: https://voidauth.example.com/oidc/token OIDC_OP_USER_ENDPOINT: https://voidauth.example.com/oidc/me OIDC_OP_LOGOUT_ENDPOINT: https://voidauth.example.com/oidc/session/end OIDC_RP_CLIENT_ID: pcl1xbprEdcbi0xQ OIDC_RP_CLIENT_SECRET: youroidcsecret OIDC_RP_SIGN_ALGO: RS256 OIDC_RP_SCOPES: "openid email" LOGIN_REDIRECT_URL: https://drive.example.com LOGIN_REDIRECT_URL_FAILURE: https://drive.example.com LOGOUT_REDIRECT_URL: https://drive.example.com OIDC_REDIRECT_ALLOWED_HOSTS: '["https://drive.example.com"]' # WOPI WOPI_CLIENTS: "collabora,onlyoffice" WOPI_COLLABORA_DISCOVERY_URL: https://collabora.example.com/hosting/discovery WOPI_ONLYOFFICE_DISCOVERY_URL: https://onlyoffice.example.com/hosting/discovery WOPI_SRC_BASE_URL: https://drive.example.com x-postgres-env: &postgres-env # Postgresql db container configuration POSTGRES_DB: drive POSTGRES_USER: drive POSTGRES_PASSWORD: 023917148a35e7c9a62964caabe0334c # 设置数据库密码 # App database configuration DB_HOST: postgresql DB_NAME: drive DB_USER: drive DB_PASSWORD: 023917148a35e7c9a62964caabe0334c # 设置数据库密码 DB_PORT: 5432 services: postgresql: image: postgres:16 restart: unless-stopped environment:注意事项:
如果你只想使用一个办公套件,例如onlyoffice,那么请修改如下变量:
WOPI_CLIENTS: "onlyoffice" #WOPI_COLLABORA_DISCOVERY_URL: https://collabora.example.com/hosting/discovery #注释掉这个变量Drive的WOPI在默认情况下会使用onlyoffice,如果你不需要collabora code可以不部署,并且onlyoffice的使用体验比collabora code好的多。
新建前端容器需要用到的NGINX配置文件:
nano default.conf.template写入如下内容:
upstream docs_backend { server ${BACKEND_HOST}:8000 fail_timeout=0; } upstream docs_frontend { server ${FRONTEND_HOST}:3000 fail_timeout=0; } server { listen 8083; server_name localhost; charset utf-8; # increase max upload size client_max_body_size 5120m; server_tokens off; proxy_ssl_server_name on; location @proxy_to_docs_backend { proxy_set_header Host $http_host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_redirect off; proxy_pass http://docs_backend; } location @proxy_to_docs_frontend { proxy_set_header Host $http_host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_redirect off; proxy_pass http://docs_frontend; } location / { try_files $uri @proxy_to_docs_frontend; } location /api { try_files $uri @proxy_to_docs_backend; } location /admin { try_files $uri @proxy_to_docs_backend; } location /media/ { # Auth request configuration auth_request /media-auth; auth_request_set $authHeader $upstream_http_authorization; auth_request_set $authDate $upstream_http_x_amz_date; auth_request_set $authContentSha256 $upstream_http_x_amz_content_sha256; # Pass specific headers from the auth response proxy_set_header Authorization $authHeader; proxy_set_header X-Amz-Date $authDate; proxy_set_header X-Amz-Content-SHA256 $authContentSha256; proxy_pass https://rustfs.example.com/drive-media-storage/; proxy_set_header Host rustfs.example.com; proxy_ssl_name rustfs.example.com; } # Proxy auth for media-preview location /media/preview/ { # Auth request configuration auth_request /media-auth; auth_request_set $authHeader $upstream_http_authorization; auth_request_set $authDate $upstream_http_x_amz_date; auth_request_set $authContentSha256 $upstream_http_x_amz_content_sha256; # Pass specific headers from the auth response proxy_set_header Authorization $authHeader; proxy_set_header X-Amz-Date $authDate; proxy_set_header X-Amz-Content-SHA256 $authContentSha256; proxy_pass https://rustfs.example.com/drive-media-storage/; proxy_set_header Host rustfs.example.com; proxy_ssl_name rustfs.example.com; } location /media-auth { proxy_pass http://docs_backend/api/v1.0/items/media-auth/; proxy_set_header X-Forwarded-Proto https; 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-Original-URL $request_uri; # Prevent the body from being passed proxy_pass_request_body off; proxy_set_header Content-Length ""; proxy_set_header X-Original-Method $request_method; } }注意将
location /media/和location /media/preview/内的域名改为自己的S3域名:proxy_pass https://rustfs.example.com/drive-media-storage/; proxy_set_header Host rustfs.example.com; proxy_ssl_name rustfs.example.com;启动全部容器:
docker compose up -d运行数据库迁移、创建Django管理员账号:
docker compose run --rm backend python manage.py migrate docker compose run --rm backend python manage.py createsuperuser --email [email protected] --password youradminpassword运行如下命令启用WOPI客户端:
docker compose run --rm backend python manage.py trigger_wopi_configuration这应该是一个BUG,按道理来说celery会自动执行,但是不知道为什么没生效。如果不执行这个命令将无法使用文档编辑功能。
配置Ferron反向代理:
nano /etc/ferron.kdl写入如下内容:
drive.example.com { proxy "http://127.0.0.1:9199/" proxy_request_header_replace "Host" "{header:Host}" } onlyoffice.example.com { proxy "http://127.0.0.1:9299/" proxy_request_header_replace "Host" "{header:Host}" } collabora.example.com { proxy "http://127.0.0.1:9980/" proxy_request_header_replace "Host" "{header:Host}" disable_url_sanitizer #true }重载Ferron:
systemctl reload ferron如遇到Ferron反向代理导致上传速度慢,可以参考这篇文章解决。
所有服务将在以下URL提供访问:
https://drive.example.com https://onlyoffice.example.com/admin https://collabora.example.com/browser/dist/admin/admin.html效果:
总结一下部署过程中遇到的问题。
1.由于官方的环境变量配置文件不完整,导致部署非常困难,甚至有些变量还要翻源码才能知道是什么作用= =
2.前端容器必须配置入口点启动脚本,否则无法使用挂载的自定义NGINX配置文件。
3.官方提供的NGINX配置文件缺少对
/media/preview/的路由,导致上传的文件无法预览。4.Ferron反向代理collabora code遇到WebSocket连接失败。原因是Ferron默认会过滤URL防止路径遍历等安全漏洞,collabora code的WebSocket连接URL里面包含
https://被Ferron过滤成https:/了,导致后端报错Bad URL。解决办法是关闭Ferron的URL过滤:disable_url_sanitizer #true5.S3实现不支持版本控制,导致文档无法保存,这个之前已经提过了。
6.S3跨域问题,因为Drive是直接操作S3,没有经过后端,所以S3这边要设置跨域规则。
7.目前需要手动执行
python manage.py trigger_wopi_configuration来集成onlyoffice等办公套件,这是一个BUG:见此issue总之就是这个项目完成度还是非常高的,基本功能都有了,要我说就是还差个文件缩略图和密码分享的功能。但是有一说一部署确实太多坑了,建议再观望一下= =
2026-02-05 21:50:50
由于Garage S3没有版本控制的功能,最近部署的几个程序都需要用到这个功能,所以部署一个RustFS当作备用。
还有一个重要原因是,RustFS可谓是历经千辛万苦,终于把这个BUG给修好了= =
RustFS和MinIO在部署和使用方面都非常相似,如果你用过MinIO,那么上手RustFS会很容易,比Garage简单的多~
准备工作:
安装Docker:
apt -y update
apt -y install curl
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
新建compose文件:
mkdir -p /opt/rustfs && cd /opt/rustfs && nano docker-compose.yml
写入如下内容:
services:
rustfs:
image: rustfs/rustfs:latest
container_name: rustfs-server
restart: unless-stopped
security_opt:
- "no-new-privileges:true"
ports:
- "127.0.0.1:9000:9000" # S3 API
- "127.0.0.1:9001:9001" # 控制台
environment:
- RUSTFS_SERVER_DOMAINS=rustfs.example.com
- RUSTFS_VOLUMES=/data/rustfs0
- RUSTFS_ADDRESS=0.0.0.0:9000
- RUSTFS_CONSOLE_ADDRESS=0.0.0.0:9001
- RUSTFS_CONSOLE_ENABLE=true
- RUSTFS_CORS_ALLOWED_ORIGINS=*
- RUSTFS_CONSOLE_CORS_ALLOWED_ORIGINS=*
- RUSTFS_ACCESS_KEY=imlala
- RUSTFS_SECRET_KEY=setyoursecretkey
- RUSTFS_REGION=auto
- RUSTFS_OBS_LOGGER_LEVEL=info
- RUSTFS_OBS_LOG_DIRECTORY=/app/logs
volumes:
- ./data:/data
- ./logs:/app/logs
healthcheck:
test: ["CMD", "sh", "-c", "curl -f http://localhost:9000/health && curl -f http://localhost:9001/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
启动:
docker compose up -d
配置Ferron反向代理:
nano /etc/ferron.kdl
写入如下内容:
rustfs.example.com {
proxy "http://127.0.0.1:9000/"
proxy_request_header_replace "Host" "{header:Host}"
}
rustfs-console.example.com {
proxy "http://127.0.0.1:9001/"
proxy_request_header_replace "Host" "{header:Host}"
}
重载Ferron:
systemctl reload ferron
如遇到Ferron反向代理导致上传速度慢,可以参考这篇文章解决。
控制台URL:https://rustfs-console.example.com
登录账号:你设置的RUSTFS_ACCESS_KEY值
登录密钥:你设置的RUSTFS_SECRET_KEY值
简单说一下虚拟主机风格(Virtual Host Style)的存储桶。我们之前已经配置了RUSTFS_SERVER_DOMAINS环境变量,域名为:rustfs.example.com,那么虚拟主机风格存储桶的命名规则应该是这样的:
bucket0.rustfs.example.com
bucket1.rustfs.example.com
bucket2.rustfs.example.com
...
实战演示一下,在RustFS控制台创建一个桶,例如名字为:photo-rustfs
编辑Ferron配置文件:
nano /etc/ferron.kdl
将之前的配置修改为如下内容:
rustfs.example.com,*.rustfs.example.com {
proxy "http://127.0.0.1:9000/"
proxy_request_header_replace "Host" "{header:Host}"
}
另外因为需要用到通配符域名证书,务必将Ferron申请证书的方式改为DNS-01,这里我配置的DNS服务商是CloudFlare,将yourkey修改成你在CloudFlare申请的key:
* {
...
// auto_tls_challenge "http-01"
auto_tls_challenge "dns-01" provider="cloudflare" api_key="yourkey"
...
}
重载Ferron:
systemctl reload ferron
这个桶现在的URL应该是:
photo-rustfs.rustfs.example.com
将桶的访问策略设置为公有,然后上传文件进行测试,如果正常应该可以访问到上传的文件:
参考:
https://docs.rustfs.com/integration/virtual.html
https://github.com/rustfs/rustfs/blob/main/docker-compose.yml
2026-01-29 17:06:56
La Suite Meet由LiveKit提供技术支持,拥有媲美Zoom的卓越性能和高质量音视频体验。
特点:
这个项目也是法国政府主导的,引用下面这段话:
On the 25th of January 2026, David Amiel, France’s Minister for Civil Service and State Reform, announced the full deployment of Visio—the French government’s dedicated Meet platform—to all public servants
这哥们打算让法国所有公务员都用这个开会= =这么牛b的程序(其实牛b的是LiveKit)必须部署逝一下。。。
本文根据官方的文档编写,主要记录并解决目前官方部署文档内的一些错误。
准备工作:
1.准备一个域名做好解析:meet.example.com(主程序),livekit.example.com(livekit服务)
2.部署VoidAuth OIDC身份验证服务,示例域名:voidauth.example.com
3.服务器的这些端口不能被其他程序占用:80、443、7880、7881、7882(UDP)、8086
在VoidAuth创建OIDC APP:
请将Redirect URLs配置为,注意URL最后的/,不要少了这个/:
https://meet.example.com/api/v1.0/callback/
请将PostLogout URL配置为,注意URL最后的/,不要少了这个/:
https://meet.example.com/api/v1.0/logout-callback/
做好上述准备工作后,现在就可以来部署Meet了,安装Docker:
apt -y update apt -y install curl curl -fsSL https://get.docker.com -o get-docker.sh sh get-docker.sh
下载compose文件以及需要用到的环境变量配置文件、LiveKit配置文件、NGINX配置文件:
cd /opt mkdir -p meet/env.d && cd meet curl -o compose.yaml https://raw.githubusercontent.com/suitenumerique/meet/refs/heads/main/docs/examples/compose/compose.yaml curl -o .env https://raw.githubusercontent.com/suitenumerique/meet/refs/heads/main/env.d/production.dist/hosts curl -o env.d/common https://raw.githubusercontent.com/suitenumerique/meet/refs/heads/main/env.d/production.dist/common curl -o env.d/postgresql https://raw.githubusercontent.com/suitenumerique/meet/refs/heads/main/env.d/production.dist/postgresql curl -o livekit-server.yaml https://raw.githubusercontent.com/suitenumerique/meet/refs/heads/main/docs/examples/livekit/server.yaml curl -o default.conf.template https://raw.githubusercontent.com/suitenumerique/meet/refs/heads/main/docker/files/production/default.conf.template
为了后续部署工作的顺利进行,这里先解决一些目前官方文档里面的错误,编辑compose文件:
nano compose.yaml
后端容器设置的env配置文件是错的,根本没有backend这个文件,取而代之的是.env文件,所以这里注释掉不存在的backend文件,添加.env文件:
...
backend:
image: lasuite/meet-backend:latest
...
restart: always
env_file:
- env.d/common
# - env.d/backend
- .env
- env.d/postgresql
...
前端容器又缺少.env文件,这里加上,同时将前端容器的8083端口暴露出来(不是官方文档说的8086端口)方便后续配置反向代理:
...
frontend:
image: lasuite/meet-frontend:latest
...
env_file:
- .env
- env.d/common
...
ports:
- "127.0.0.1:8086:8083"
将LiveKit容器的7880端口暴露出来,这是Likvkit的“信号”端口,是Meet与Livekit通信的关键:
...
livekit:
image: livekit/livekit-server:latest
command: --config /config.yaml
ports:
- 127.0.0.1:7880:7880
- 7881:7881/tcp
- 7882:7882/udp
...
编辑供前端容器内部使用的NGINX配置文件:
nano default.conf.template
将这两个错误的环境变量:BACKEND_HOST/FRONTEND_HOST修改为如下内容:
upstream meet_backend {
server ${BACKEND_INTERNAL_HOST}:8000 fail_timeout=0;
}
upstream meet_frontend {
server ${FRONTEND_INTERNAL_HOST}:8080 fail_timeout=0;
}
编辑.env文件:
nano .env
这里只列出需要修改的内容:
MEET_HOST=meet.example.com #KEYCLOAK_HOST=id.domain.tld # 注释掉这个配置,我们不使用KEYCLOAK LIVEKIT_HOST=livekit.example.com #REALM_NAME=meet # 注释掉这个配置,我们不使用KEYCLOAK
编辑common文件:
nano env.d/common
这里只列出需要修改的内容:
# Django DJANGO_SECRET_KEY= # 使用openssl rand -hex 32生成 # Mail DJANGO_EMAIL_HOST=mail.example.com DJANGO_EMAIL_HOST_USER=smtp DJANGO_EMAIL_HOST_PASSWORD=smtppassword DJANGO_EMAIL_PORT=587 [email protected] DJANGO_EMAIL_USE_TLS=true # OIDC OIDC_OP_JWKS_ENDPOINT=https://voidauth.example.com/oidc/jwks OIDC_OP_AUTHORIZATION_ENDPOINT=https://voidauth.example.com/oidc/auth OIDC_OP_TOKEN_ENDPOINT=https://voidauth.example.com/oidc/token OIDC_OP_USER_ENDPOINT=https://voidauth.example.com/oidc/me OIDC_OP_LOGOUT_ENDPOINT=https://voidauth.example.com/oidc/session/end OIDC_RP_CLIENT_ID= OIDC_RP_CLIENT_SECRET= # Livekit Token settings LIVEKIT_API_SECRET= # 使用openssl rand -hex 32生成
编辑postgresql文件,设置PostgreSQL数据库密码:
nano env.d/postgresql
这里只列出需要修改的内容:
DB_PASSWORD=setyourdbpassword # 设置你的数据库密码。
编辑livekit-server.yaml:
nano livekit-server.yaml
将keys的值设置成和LIVEKIT_API_SECRET相同的内容:
port: 7880 redis: address: redis:6379 keys: meet: # 把这里的值设置成和LIVEKIT_API_SECRET相同的内容 # WebRTC configuration rtc: # # when set, LiveKit will attempt to use a UDP mux so all UDP traffic goes through # # listed port(s). To maximize system performance, we recommend using a range of ports # # greater or equal to the number of vCPUs on the machine. # # port_range_start & end must not be set for this config to take effect udp_port: 7882 # when set, LiveKit enable WebRTC ICE over TCP when UDP isn't available # this port *cannot* be behind load balancer or TLS, and must be exposed on the node # WebRTC transports are encrypted and do not require additional encryption # only 80/443 on public IP are allowed if less than 1024 tcp_port: 7881 # use_external_ip should be set to true for most cloud environments where # the host has a public IP address, but is not exposed to the process. # LiveKit will attempt to use STUN to discover the true IP, and advertise # that IP with its clients use_external_ip: true
启动全部容器:
docker compos up -d
运行数据库迁移并创建Django管理员用户:
docker compose run --rm backend python manage.py migrate docker compose run --rm backend python manage.py createsuperuser --email [email protected] --password adminpassword
稍后你可以使用这个URL访问Docs的Django后台:meet.example.com/admin。在这个后台你可以管理用户创建的房间。
请注意这里创建的用户只拥有Django管理员权限,最终实际可供用户使用的账号必须使用OIDC创建。
配置Ferron反向代理:
nano /etc/ferron.kdl
写入如下内容:
meet.example.com {
proxy "http://127.0.0.1:8086/"
proxy_request_header_replace "Host" "{header:Host}"
}
livekit.example.com {
proxy "http://127.0.0.1:7880/"
proxy_request_header_replace "Host" "{header:Host}"
}
如果你使用的是别的反向代理,则必须为LiveKit配置WebSocket和长连接支持。Ferron默认支持这些,所以不必写在配置文件内。
重载Ferron:
systemctl reload ferron
效果:
看着这个经典的蓝白配色UI,还有这几个按钮的造型,然后还是法国出品,瞬间就觉得好似一位故人:OVH!
简单测试,在电脑上启用OBS虚拟摄像头,在手机上加入房间,看看双方能不能正常视频通话:
功能还是很全面的,共享浏览器屏幕、发表情、拍手、甚至还有虚拟背景和聊天功能。唯独在国内使用的话可能要注意连通性问题,国内的网络嘛,懂的都懂,如果你按照这篇文章部署后连不上,则可能还需要在LiveKit配置TURN服务。
2026-01-28 19:12:14
Docs这个项目我一直都在关注,奈何之前的部署方式只支持k8s,如果只是为了部署一个笔记软件去搭一个k8s的话,那真的是有点拿大炮打蚊子那味了= =最近发现这个项目支持用docker compose部署了,遂记录下部署过程与遇到的一些问题。
不得不说国家队还是牛B!这个笔记软件第一次用就感觉挺顺手的,前端UI简洁大气,自带AI功能,可以帮你美化、改写、总结、修正错别字、翻译等。自托管的实例所有功能都能使用,与官方实例1:1还原,而不是像某些“开源笔记”那样吃相难看。
缺点也还是有的,没有十全十美的东西= =目前虽然支持docker compose部署了,但部署起来还是比较麻烦。另外我在使用中发现一些小bug,例如:上传的文件名不能包含中文,有中文就会导致上传失败。导出的PDF文件显示中文是乱码,且不支持代码块等。如果你不介意这些问题的话,可以尝试一下。
本文根据Docs官方的文档编写,进行了一些修改:
1.将Keycloak更换为VoidAuth,因为Keycloak部署和配置都过于复杂。
2.将MinIO更换为Garage,因为开源的MinIO已经名存实亡了。
3.不使用官方的NGINX反向代理配置,因为我的服务器已经运行Ferron Web Server了。
如果你完全遵循本文的步骤来部署,则在开始前需要做好以下准备工作:
1.一个域名做好解析,本文示例域名:docs.example.com
2.部署Garage S3对象存储。本文示例域名:s3-garage.example.com
注:如果你使用Garage S3则无法使用Docs的历史版本与回退功能,这是由于目前Garage的S3实现不支持“版本控制”功能导致的,并不是Docs的问题。
3.部署VoidAuth OIDC身份验证服务。本文示例域名:voidauth.example.com
4.创建一个OPENAI的API KEY。
在Garage S3创建存储桶和key并授权:
docker exec -ti garage /garage bucket create docs-media-storage docker exec -ti garage /garage key create docs-media-storage-app-key docker exec -ti garage /garage bucket allow --read --write --owner docs-media-storage --key docs-media-storage-app-key
在VoidAuth创建OIDC APP:
务必将Redirect URLs设置为:
https://docs.example.com/api/v1.0/callback/
务必将PostLogout URL设置为:
https://docs.example.com/api/v1.0/logout-callback/
做好上面的准备工作后,现在可以正式部署Docs了,先在服务器内安装Docker:
apt -y update apt -y install curl curl -fsSL https://get.docker.com -o get-docker.sh sh get-docker.sh
下载compose文件以及需要用到的环境变量配置文件、NGINX配置文件:
cd /opt mkdir -p docs/env.d cd docs curl -o compose.yaml https://raw.githubusercontent.com/suitenumerique/docs/refs/heads/main/docs/examples/compose/compose.yaml curl -o env.d/common https://raw.githubusercontent.com/suitenumerique/docs/refs/heads/main/env.d/production.dist/common curl -o env.d/backend https://raw.githubusercontent.com/suitenumerique/docs/refs/heads/main/env.d/production.dist/backend curl -o env.d/yprovider https://raw.githubusercontent.com/suitenumerique/docs/refs/heads/main/env.d/production.dist/yprovider curl -o env.d/postgresql https://raw.githubusercontent.com/suitenumerique/docs/refs/heads/main/env.d/production.dist/postgresql curl -o default.conf.template https://raw.githubusercontent.com/suitenumerique/docs/refs/heads/main/docker/files/production/etc/nginx/conf.d/default.conf.template
设置PostgreSQL数据库密码:
nano env.d/postgresql
这里只列出需要修改的内容:
DB_PASSWORD=setyourdbpassword # 设置你的数据库密码。
设置YPROVIDER API KEY/COLLABORATION SECRET:
nano env.d/yprovider
这里只列出需要修改的内容:
Y_PROVIDER_API_KEY=randomkey # 使用openssl rand -hex 32生成 COLLABORATION_SERVER_SECRET=randomkey # 使用openssl rand -hex 32生成
编辑common环境变量配置文件:
nano env.d/common
这里只列出需要修改的内容:
DOCS_HOST=docs.example.com # 设置你的Docs实例域名 #KEYCLOAK_HOST=id.domain.tld # 注释掉这个变量,因为我们不使用KEYCLOAK S3_HOST=s3-garage.example.com # 设置你的Garage S3端点域名 BUCKET_NAME=docs-media-storage # 如果你的S3桶名不是docs-media-storage请修改 #REALM_NAME=docs # 注释掉这个变量,因为我们不使用KEYCLOAK
编辑backend环境变量配置文件:
nano env.d/backend
这里只列出需要修改和增加的内容:
## Django DJANGO_SECRET_KEY= # 使用openssl rand -hex 32生成 DOCUMENT_IMAGE_MAX_SIZE=2048000000 # 增加这个配置,修改默认文件上传大小到2048MB # Mail DJANGO_EMAIL_HOST=mail.example.com DJANGO_EMAIL_HOST_USER=smtp DJANGO_EMAIL_HOST_PASSWORD=smtppassword DJANGO_EMAIL_PORT=587 [email protected] DJANGO_EMAIL_USE_TLS=true # Media AWS_S3_REGION_NAME=garage # 因Garage S3强制要求区域,增加这个配置以适配Garage S3 AWS_S3_ACCESS_KEY_ID= AWS_S3_SECRET_ACCESS_KEY= # OIDC OIDC_OP_JWKS_ENDPOINT=https://voidauth.example.com/oidc/jwks OIDC_OP_AUTHORIZATION_ENDPOINT=https://voidauth.example.com/oidc/auth OIDC_OP_TOKEN_ENDPOINT=https://voidauth.example.com/oidc/token OIDC_OP_USER_ENDPOINT=https://voidauth.example.com/oidc/me OIDC_OP_LOGOUT_ENDPOINT=https://voidauth.example.com/oidc/session/end OIDC_RP_CLIENT_ID= OIDC_RP_CLIENT_SECRET= # AI AI_FEATURE_ENABLED=true # is false by default AI_BASE_URL=https://api.openai.com/v1 AI_API_KEY=sk-proj-3aFFuEBT... AI_MODEL=gpt-4o-mini
编辑NGINX配置文件:
nano default.conf.template
修改上传大小与之前的配置对应:
# increase max upload size client_max_body_size 2048m;
为了避免混淆,请注意这里的NGINX配置文件是给前端容器在容器内部使用的,并不是给主机NGINX用的。无论你用不用NGINX反代,都必须要有这个文件。
编辑compose文件:
nano compose.yaml
将前端容器的8083端口暴露出来:
...
frontend:
image: lasuite/impress-frontend:latest
...
volumes:
- ./default.conf.template:/etc/nginx/templates/docs.conf.template
ports:
- 127.0.0.1:8083:8083
...
启动Docs:
docker compose up -d
运行数据库迁移并创建Django管理员用户:
docker compose run --rm backend python manage.py migrate docker compose run --rm backend python manage.py createsuperuser --email [email protected] --password adminpassword
稍后你可以使用这个URL访问Docs的Django后台:docs.example.com/admin。请注意这里创建的用户仅作为Django管理员账号,与实际的用户账号不相关,用户账号始终使用OIDC登录。
配置Ferron反向代理:
nano /etc/ferron.kdl
写入如下内容:
docs.example.com {
proxy "http://127.0.0.1:8083/"
proxy_request_header_replace "Host" "{header:Host}"
}
重载Ferron:
systemctl reload ferron
看下使用效果,和AFFiNE一样,支持使用/命令呼出菜单:
选中一段内容可以使用AI功能:
AI改写:
用作提示:
gpt-4o-mini这排版感觉比我写的还好啊= =我艹了个DJ!把功能都试的差不多了,最后再补充点不太满意的地方。搜索功能弱了点,似乎不支持全文搜索。不支持密码分享文档。没有个人账号的信息页面,缺少上传头像之类的功能,这方面有点过于简洁了= =总的来说很牛B,给我一种很丝滑的体验。
2026-01-25 21:32:12
TrustTunnel可以将任意网络流量(TCP/UDP/ICMP)通过加密的HTTP/2或HTTP/3连接进行隧道传输。该协议的设计目标如下:
隐蔽性:流量显示为标准的HTTPS,难以与常规网页浏览区分开来。
高性能:在单个传输会话上高效复用多个连接。
可靠性:内置会话恢复和健康检查机制。
灵活性:同时支持 HTTP/2(基于TLS)和HTTP/3(基于QUIC)传输协议。
TrustTunnel还有iOS/Android的GUI客户端,Windows和Linux目前只有CLI客户端,但是这个CLI也支持TUN模式。我部署试了一下,目前感觉分流不太行,要是官方能支持GeoIP之类的数据库就好了,现在只能用IPCIDR= =
开始部署前你需要先准备一个域名,做好解析记录,例如:giatunnel.example.com。一台Debian x64服务器(VPS)。
Debian服务器端安装:
cd /opt wget https://github.com/TrustTunnel/TrustTunnel/releases/download/v0.9.115/trusttunnel-v0.9.115-linux-x86_64.tar.gz tar -xzvf trusttunnel-v0.9.115-linux-x86_64.tar.gz mv trusttunnel-v0.9.115-linux-x86_64 trusttunnel cd trusttunnel/
直接运行这个二进制文件开始配置向导,这个向导会协助你配置服务端的内容:
./setup_wizard
1.服务端监听的地址和端口,默认:0.0.0.0:443,但是考虑到443端口可能会被Web Server等软件占用,本文配置为:0.0.0.0:8443。
2.设置用户账号和密码,这里的账号和密码用于客户端连接。TrustTunnel是支持多用户的,这可能为后续的机场服务铺路= =
3.创建一个规则配置文件(rules.toml)如果你是一个人使用,那么服务器端不需要配置这个,一路回车下一步即可。
4.创建一个核心配置文件(vpn.toml)所有与VPN相关的配置都在这个配置文件内。一路回车下一步即可!
5.协助你配置域名证书,可使用Let’s Encrypt自动申请证书,支持HTTP-01/DNS-01模式。如果服务器80端口被Web Server等软件占用,可以选择DNS-01模式。
如果选择DNS-01模式,则需要在DNS解析提供商处添加相应解析记录:
配置完成后,复制一份systemd服务配置文件:
cp trusttunnel.service.template /etc/systemd/system/trusttunnel.service
启动TrustTunnel服务并设置开机自启:
systemctl enable --now trusttunnel
检查运行状态,确保正常运行:
systemctl status --now trusttunnel
[可选]导出客户端配置文件,方便客户端配置,例如本文之前配置的用户账号是imlala,服务器IP是89.64.19.37,端口为8443,那么执行如下命令:
./trusttunnel_endpoint vpn.toml hosts.toml -c imlala -a 89.64.19.37:8443
会输出类似如下内容,这些是配置客户端连接需要的,可以复制保存下来:
# Endpoint host name, used for TLS session establishment hostname = "giatunnel.exampel.com" # Endpoint addresses. addresses = ["89.64.19.37:8443"] # Whether IPv6 traffic can be routed through the endpoint has_ipv6 = true # Username for authorization username = "imlala" # Password for authorization password = "yourvpnpassword" # Skip the endpoint certificate verification? # That is, any certificate is accepted with this one set to true. skip_verification = false upstream_protocol = "http2" upstream_fallback_protocol = "" anti_dpi = false
服务器端的配置就全部完成了,接下来配置客户端。这里我以Linux的CLI客户端为例。
下载客户端压缩包解压进入到目录:
cd /home/imlala/桌面 wget https://github.com/TrustTunnel/TrustTunnelClient/releases/download/v0.99.93/trusttunnel_client-v0.99.93-linux-x86_64.tar.gz tar -xzvf trusttunnel_client-v0.99.93-linux-x86_64.tar.gz cd trusttunnel_client-v0.99.93-linux-x86_64
执行配置向导,协助你配置客户端:
./setup_wizard
1.配置VPN的运行模式,目前支持general和selective模式。general是指加入排除列表的都走直连,其他走代理。而selective是加入排除列表的走代理,其他走直连。本文使用的是general模式。
2.配置连接到VPN服务器的IP地址/端口/账号密码,对于本文而言端口号是8443。
3.配置证书,直接回车下一步。但是请不要启用跳过证书验证,这可能会导致安全问题,如果你是自签证书的话就无所谓了。
4.配置服务端的域名,本文示例:giatunnel.example.com
5.配置客户端监听类型,支持Socks5和TUN,本文使用的是TUN。
接下来使用文本编辑器打开向导生成的客户端配置文件:
nano trusttunnel_client.toml
配置一个DNS服务器:
dns_upstreams = ["tls://1.1.1.1"]
[可选]配置分流,下载中国的IP列表,然后新建一个Python脚本,内容如下:
with open("chnroute.txt", "r") as f:
ips = [f'"{line.strip()}"' for line in f if line.strip()]
with open("config.toml", "w") as f:
f.write("exclusions = [\n ")
f.write(",\n ".join(ips))
f.write("\n]")
print("处理完成,结果已保存至 config.toml")
运行脚本,把IP转换成TrustTunnel需要的格式:
python3 ipcidr.py
把转换好的内容全选复制粘贴到trusttunnel_client.toml:
exclusions = [ "1.0.1.0/24", "1.0.2.0/23", "1.0.8.0/21", "1.0.32.0/19", "1.1.0.0/24", "1.1.2.0/23", "1.1.4.0/22", "1.1.8.0/24", "1.1.9.0/24", "1.1.10.0/23", "1.1.12.0/22", "1.1.16.0/20", "1.1.32.0/19", "1.2.0.0/23", "1.2.2.0/24", ... ]
下载中国域名列表,然后新建一个Python脚本,内容如下:
import json
def extract_domain_suffixes(input_file, output_file):
try:
# 1. 读取并解析 JSON 文件
with open(input_file, 'r', encoding='utf-8') as f:
data = json.load(f)
all_suffixes = []
# 2. 遍历 rules 提取所有的 domain_suffix
rules = data.get("rules", [])
for rule in rules:
if "domain_suffix" in rule:
all_suffixes.extend(rule["domain_suffix"])
if not all_suffixes:
print("未找到任何 domain_suffix 数据。")
return
# 3. 格式化处理
formatted_list = []
total_count = len(all_suffixes)
for i, item in enumerate(all_suffixes):
# 在最前面添加两个空格
line = f' "{item}"'
# 如果不是最后一项,则在末尾加逗号
if i
运行脚本,把域名转换成TrustTunnel需要的格式:
python3 cndomain.py
把转换好的内容全选复制粘贴到trusttunnel_client.toml:
exclusions = [
...
"zzzla.com",
"zzzlsh.com",
"zzznkq.com",
"zzzppp.com",
"zzzqqp.com",
"zzzsxx.com",
"zzzxwh.net",
"zzzyb.com",
"zzzymjg.com",
"zzzyy.com",
"zzzyyy.com",
"zzzzaaaa.com",
"zzzzzz.me"
]
启动TrustTunnel客户端:
sudo ./trusttunnel_client -c trusttunnel_client.toml
使用TUN模式请使用sudo提升权限,以允许TrustTunnel创建TUN0虚拟网卡及配置路由表。
简单说一下使用体验(分流效果)。。如果客户端使用:
upstream_protocol = "http2"
访问国外的站点很快,但是国内站点比mihomo/sing-box慢多了,分流是生效了的,不知道是不是DNS的问题。。如果客户端使用:
upstream_protocol = "http3"
则访问国外也慢的一匹,应该是奠信限制udp导致的= =,另外请不要把油管4k/8k视频当作测速工具,即便要测也请关闭浏览器的quic(血泪史):
chrome://flags/#enable-quic
2026-01-19 17:12:01
我几年前写过一篇部署PowerDNS权威服务器的文章,最近需要搭建一个,然后翻出来看了下发现写的不够完善,而且纯手动部署太麻烦了,遂决定重新记录下使用Docker部署的步骤,另外主要补充一下:主从同步、DNSSEC的配置。
首先还是需要到域名注册商添加胶水记录(Glue Record),我使用的是spaceship,这家的用户面板不叫胶水记录,叫个人名称服务器,说实话有时候挺烦这种自己瞎起名字的行为。。害我在他们的这个面板里面找半天:
由于spaceship的限制,这里至少要添加2条胶水记录,如果你不打算部署2台服务器(主从同步),可以把两条记录的值都指向同一台服务器的IP,之后更改域名的NS服务器为刚设置的“个人名称服务器”:
本文示例:91.99.72.72为主(primary)服务器,49.13.168.202为从(secondary)服务器,在两台服务器内都安装Docker:
apt -y update apt -y install curl curl -fsSL https://get.docker.com -o get-docker.sh sh get-docker.sh
在主(primary)服务器创建目录新建compose文件:
mkdir /opt/pdns-mysql && cd /opt/pdns-mysql && nano docker-compose.yml
写入如下内容,需要修改的地方写了注释:
services:
mariadb:
image: mariadb:lts
container_name: pdns-mariadb
restart: unless-stopped
networks:
- pdns
environment:
- MARIADB_ROOT_PASSWORD=setyourpdnsmasterdbpassword # 设置数据库ROOT用户的密码
volumes:
- ./mariadb-data:/var/lib/mysql:Z
healthcheck:
test: ['CMD', 'healthcheck.sh', '--connect', '--innodb_initialized']
timeout: 10s
retries: 5
phpmyadmin:
image: phpmyadmin
restart: unless-stopped
networks:
- pdns
ports:
- 8988:80
environment:
- PMA_HOST=mariadb
pdns-master:
image: pschiffe/pdns-mysql:latest
container_name: pdns-master
hostname: ns1.ohsb.cc # 设置PDNS主服务器的主机名,务必与你的胶水记录保持一致
restart: unless-stopped
networks:
pdns:
ipv4_address: 172.89.64.74
depends_on:
mariadb:
condition: service_healthy
environment:
- PDNS_gmysql_host=mariadb
- PDNS_gmysql_port=3306
- PDNS_gmysql_user=root
- PDNS_gmysql_password=setyourpdnsmasterdbpassword # 设置连接数据库ROOT用户的密码
- PDNS_gmysql_dbname=powerdns
- PDNS_gmysql_dnssec=yes
- PDNS_primary=yes
- PDNS_api=yes
- PDNS_api_key=setyourpdnsapikey # 设置PDNS API KEY
- PDNS_webserver=yes
- PDNS_webserver_address=0.0.0.0
- PDNS_webserver_allow_from=0.0.0.0/0,::/0
- PDNS_webserver_password=setyourwebserverpassword # 设置PDNS WEB密码
- PDNS_version_string=anonymous
- PDNS_default_ttl=1500
- PDNS_allow_axfr_ips=49.13.168.202 # 设置为从服务器的公网IP
- PDNS_only_notify=49.13.168.202 # 设置为从服务器的公网IP
ports:
- '53:53'
- '53:53/udp'
- '8081:8081'
volumes:
- /etc/localtime:/etc/localtime:ro
pdns-admin:
image: pschiffe/pdns-admin
container_name: pdns-admin
restart: unless-stopped
depends_on:
mariadb:
condition: service_healthy
networks:
- pdns
ports:
- '8989:8080'
environment:
- PDNS_ADMIN_SQLA_DB_HOST=mariadb
- PDNS_ADMIN_SQLA_DB_PORT=3306
- PDNS_ADMIN_SQLA_DB_USER=root
- PDNS_ADMIN_SQLA_DB_PASSWORD=setyourpdnsmasterdbpassword # 设置连接数据库ROOT用户的密码
- PDNS_ADMIN_SQLA_DB_NAME=powerdnsadmin
- PDNS_API_URL="http://pdns-master:8081/"
- PDNS_API_KEY=setyourpdnsapikey # 设置连接到PDNS Master的API KEY
- PDNS_VERSION=5.0
volumes:
- /etc/localtime:/etc/localtime:ro
networks:
pdns:
ipam:
config:
- subnet: 172.89.0.0/16
gateway: 172.89.0.1
在从(secondary)服务器创建目录新建compose文件:
mkdir /opt/pdns-slave && cd /opt/pdns-slave && nano docker-compose.yml
写入如下内容,需要修改的地方写了注释:
services:
mariadb:
image: mariadb:lts
container_name: pdns-mariadb
restart: unless-stopped
networks:
- pdns
environment:
- MARIADB_ROOT_PASSWORD=setyourpdnsmasterdbpassword # 设置数据库ROOT用户的密码
volumes:
- ./mariadb-data:/var/lib/mysql:Z
healthcheck:
test: ['CMD', 'healthcheck.sh', '--connect', '--innodb_initialized']
timeout: 10s
retries: 5
phpmyadmin:
image: phpmyadmin
restart: unless-stopped
networks:
- pdns
ports:
- 8988:80
environment:
- PMA_HOST=mariadb
pdns-slave:
image: pschiffe/pdns-mysql:latest
container_name: pdns-slave
hostname: ns2.ohsb.cc # 设置PDNS从服务器的主机名,务必与你的胶水记录保持一致,从服务器必须设置,否则无法与主服务器同步!
restart: unless-stopped
networks:
pdns:
ipv4_address: 172.89.64.75
depends_on:
mariadb:
condition: service_healthy
environment:
- PDNS_gmysql_host=mariadb
- PDNS_gmysql_port=3306
- PDNS_gmysql_user=root
- PDNS_gmysql_password=setyourpdnsmasterdbpassword # 设置连接数据库ROOT用户的密码
- PDNS_gmysql_dbname=powerdnsslave
- PDNS_gmysql_dnssec=yes
- PDNS_secondary=yes
- PDNS_autosecondary=yes
- PDNS_webserver=yes
- PDNS_webserver_address=0.0.0.0
- PDNS_webserver_allow_from=0.0.0.0/0,::/0
- PDNS_webserver_password=setyourwebserverpassword # 设置PDNS WEB密码
- PDNS_version_string=anonymous
- PDNS_default_ttl=1500
- PDNS_disable_axfr=yes
- PDNS_allow_notify_from=91.99.72.72 # 设置为主服务器的公网IP
- SUPERMASTER_IPS=91.99.72.72 # 设置为主服务器的公网IP
ports:
- '53:53'
- '53:53/udp'
- '8081:8081'
volumes:
- /etc/localtime:/etc/localtime:ro
networks:
pdns:
ipam:
config:
- subnet: 172.89.0.0/16
gateway: 172.89.0.1
配置项太多,这里我也懒得详细说明了,打字太累= =为了方便理解,我没有给敏感内容(域名、服务器IP等信息)脱敏,这套配置是我目前从测试服务器1:1复制下来的。只要你按照注释来配置,肯定能跑起来的,并且功能都是正常的。
启动主、从服务器的所有服务:
docker compose up -d
配置DNSSEC,首先打开PowerDNS-Admin(91.99.72.72:8989)注册一个账号,第一个注册的账号自动成为管理员。
在PowerDNS-Admin添加Zone,Zone Name:你的域名,Zone Type选择:Primary,一定要选择Primary,Primary,Primary!
按如图所示添加2条NS记录以及2条A记录:
启用DNSSEC:
会回显类似如图的信息:
DNSKEY不用管,你可以简单理解为这是公钥。我们需要注意的是DS下面的内容,这实际上代表两条DS记录:
41411 13 2 48e9394892ee2da8... 41411 13 4 9a9382822735e648...
41411是“密钥标签”,13是“算法”,2和4是“摘要类型”,后面一长串是“摘要”。按照这个格式在spaceship内添加两条DS记录:
实际上只添加一条DS记录也是可以的,这个取决于你自己。检查DNSSEC是否生效,可以安装如下软件包:
apt install bind9-dnsutils
测试:
delv ns1.ohsb.cc delv ns2.ohsb.cc
如果输出的内容有fully validated,则说明DNSSEC工作正常:
检查主从同步是否正常,可以使用compose内部署的phpmyadmin登录到服务器数据库,查看两个数据库内的数据是否一致:
总结下部署过程中遇到的问题。从服务器无法同步,报错:
Unable to find backend willing to host ohsb.cc for potential autoprimary 91.99.72.72. Remote nameservers
这是从服务器的Docker容器没有设置正确的hostname导致的,见此issue。
在spaceship设置了DNSSEC的DS记录后,spaceship面板的DNS传播状态异常。这是由于PowerDNS内的NS记录配置错误导致的。我在spaceship配置了两个NS服务器,那么PowerDNS-Admin内也应该有两条NS记录,必须要保持一致。
参考资料:
https://hub.docker.com/r/pschiffe/pdns-mysql
https://github.com/pschiffe/docker-pdns/blob/master/docker-compose-mysql.yml
https://doc.powerdns.com/authoritative/settings.html
https://doc.powerdns.com/authoritative/backends/generic-mysql.html#gmysql-dnssec