news 2026/9/26 11:57:41

用Docker Compose部署OnlyOffice:架构、配置与避坑实践指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用Docker Compose部署OnlyOffice:架构、配置与避坑实践指南

在公司内网搭一套能在线打开、编辑、协同处理 Word、Excel、PPT 的文档服务,OnlyOffice 几乎是最绕不开的选择;只要你的机器上装了 Docker,用 Docker Compose 部署 OnlyOffice 又是目前公认最省心的一条路。官方镜像把 Nginx、Node.js、PostgreSQL、Redis 这些依赖都打在一个容器里,写一份十几行的 yaml 就能把整套服务拉起来。

这篇文章不打算只贴一份 docker-compose.yml 了事。我会把自己在实际部署里踩过的坑、反复确认过的配置项、以及像 JWT 密钥、持久化卷、反向代理、Moodle 集成这类容易翻车的地方全部串起来讲清楚。适合正在做 OA、网盘、知识库集成的开发,也适合想在实验环境快速体验在线协同编辑的运维同学。

1. 为什么用 Docker Compose 部署 OnlyOffice:架构与选型思路

1.1 OnlyOffice 到底解决什么问题

OnlyOffice 是一套开源的在线办公套件,核心能力是让用户直接在浏览器里打开和编辑 docx、xlsx、pptx 等 Office 格式文档,同时支持多人协同编辑、批注评论、版本历史。和本地 Office 最大的区别在于:你不需要把文件下载到桌面再上传回去,浏览器里改完,服务端负责保存和回调,整个编辑链路都在你自建的服务里完成。

这套东西最常见的落地位置有三类:一类是私有网盘或知识库里的在线预览和编辑,比如 Nextcloud、Seafile;一类是 OA/ERP 里的附件预览,让审批附件不用下载就能看;还有一类是教育平台里的作业批改,比如 Moodle 里直接打开 Word 文档批注。说到底,它解决的是“文档内容不出内网”的问题,数据在自己手里,离线可用,也方便做权限控制。

1.2 Docker Compose 方案为什么比裸装更省心

OnlyOffice 底层不是一个单进程应用。它需要 Nginx 做入口,Node.js 跑文档服务,PostgreSQL 存元数据,Redis 做缓存,RabbitMQ 做队列。如果裸装,你得手动把这些依赖一个个准备好,版本不匹配、环境变量漏配、端口冲突,任何一个环节都可能让你半天起不来。

官方提供的 onlyoffice/documentserver 镜像把这些服务全部封进一个容器里,容器启动时由内部进程管理器统一拉起。但问题是,直接用 docker run 部署的命令越来越长,环境变量、卷挂载、网络参数混在一起,时间久了根本没法维护。Docker Compose 干的就是把这一堆参数写进声明式配置,一条 docker compose up -d 全部搞定。它带来的好处很直接:配置可以放进 Git 做版本管理,换机器可以一键恢复,团队协作时大家用同一份 yaml 而不是复制粘贴长命令。

1.3 部署拓扑:最小与生产两种形态

最简部署只需要一个文档服务器容器加几个数据卷。官方镜像在容器内部已经把 PostgreSQL、Redis、RabbitMQ 都内置了,所以你不需要额外跑数据库容器。对中小团队、测试环境、内部工具来说,这种单容器形态是最稳的,因为少一个组件就少一类故障。

生产环境如果想拆细,可以把 PostgreSQL、Redis、RabbitMQ 分别用独立容器或外部托管,再把 OnlyOffice 容器通过环境变量指过去。这么做的收益是资源隔离更彻底、数据库可以单独备份扩容,但维护成本也上来了。我的建议是:初期先用单容器,跑通业务后再按需拆分,别一上来就把整个架构复杂化。

流量路径大致是这样:外部请求先到反向代理(443),代理转发到宿主机映射端口(比如 8080),再进入容器内部 Nginx 的 80 端口。容器内部再路由到文档服务、转换服务或者协同编辑的 WebSocket 服务。理解这条链路,后面排查 502、WebSocket 连不上才有清晰的方向。

2. 从零编写 docker-compose.yml:核心配置逐项拆解

2.1 镜像版本:latest 还是固定 tag

示例配置里我用的是 onlyoffice/documentserver:latest,因为演示方便、第一次拉取不需要查版本号。但生产环境我强烈建议固定一个具体 tag。原因很简单:OnlyOffice 升级有时候会改存储结构、数据库迁移脚本、JWT 策略,你昨天还跑得好好的镜像,今天重新 pull 一下可能就起不来了。固定 tag 后,升级变成你主动选择的一个动作,而不是某次重建容器时被动的意外。

镜像体积非常大,印象里解压后好几个 GB,拉取时间也比一般镜像长。内网环境如果拉不动,正确做法是找一台能联网的机器先 docker pull,再 docker save 成 tar 包导入内网,后面我会专门说。

另外要注意这个容器对内存不客气。因为它内部同时跑着数据库、缓存、队列、文档服务,实测内存小于 2GB 时启动很容易失败,或者用一段时间被 OOM 杀死。个人经验是至少 4GB 内存起步,生产场景建议 4 核 8GB。

2.2 端口、网络与容器名

容器内部 Nginx 监听的是 80 端口。宿主机如果已经有 Nginx、Apache 或者别的 Web 服务占了 80,就把对外端口映射成 8080 或某个高位端口。这里有个很多人不理解的点:你暴露的是宿主机端口,不是容器内端口,所以 8080:80 表示访问宿主机 8080 就等价于访问容器内 80。

容器名固定有一个好处:日志、备份、docker exec 排查时不用每次去查随机 ID。restart 策略我统一用 unless-stopped,意思是容器异常退出会自动拉起,但你手动 stop 后它不会自己跑起来,很适合长期运行的服务。

网络方面建议创建一个独立 bridge 网络。如果你的 OnlyOffice 要对接 Moodle、Nextcloud 或者其他容器化应用,把这些服务放进同一个 docker network,就可以用容器名互相访问,不需要把业务服务端口完全暴露到宿主机。

2.3 JWT 与安全项:最容易翻车的三个点

OnlyOffice 从 7.2 版本开始默认把 JWT 鉴权拉得很紧。文档编辑器、文档转换服务、回调通知之间会通过 JWT 签名做身份校验,一旦启用,所有请求都得带上正确的签名,否则直接 401 或拒绝处理。

环境变量里最关键的是这三个:JWT_ENABLED 控制开关,JWT_SECRET 是签名密钥,JWT_HEADER 指定签名放在请求头的哪个字段。集成的第三方系统必须和 OnlyOffice 使用完全相同的 JWT_SECRET,否则编辑器加载不出来、保存回调失败、转换接口报错,问题看起来五花八门,根源往往就是这个密钥对不上。

密钥生成可以用 openssl rand -hex 32,32 字节的随机十六进制字符串足够安全。如果你是在内网使用,还需要注意一个反向场景:容器的回调请求目标是某个内网地址时,可能会被 OnlyOffice 内部的“拒绝访问私有 IP”策略拦掉。遇到这种情况,可以加上环境变量 ALLOW_PRIVATE_IP_ADDRESS=true,让容器允许回调到 192.168 / 10 / 172.16 这类私有网段。

2.4 持久化卷:哪些目录必须挂

不挂卷的容器就像一次性用品,容器一删数据全没。OnlyOffice 官方镜像声明的持久化目录有这几个:

容器内路径作用丢失影响
/var/www/onlyoffice/Data证书、配置、文件存储需要重新配置,自定义证书丢失
/var/log/onlyoffice运行日志排查问题没有历史日志
/var/lib/onlyoffice文档缓存和部分运行数据缓存失效,需要重新渲染
/var/lib/postgresqlPostgreSQL 数据目录版本历史、文档元数据全部丢失
/var/lib/rabbitmqRabbitMQ 数据队列任务状态丢失
/var/lib/redisRedis 持久化数据缓存数据丢失

很多人在意文件本体放哪,其实 OnlyOffice 本身一般不存源文件,源文件由集成方(Moodle、网盘、OA)来保管。但如果版本历史、协同编辑状态、文档转换缓存这些没了,体验会大打折扣。特别是 /var/lib/postgresql,版本历史记录就在数据库里,想保留“谁在什么时候改过哪里”,这个卷必须挂好。

Docker 的命名卷(named volume)迁移方便,适合只通过 docker compose 管理;绑定挂载(bind mount)适合你想直接用 vim 看日志、备份文件。我一般用命名卷,然后用 docker run --volumes-from 的方式做备份,后面实操部分会写具体命令。

2.5 一份可直接复制的最小 compose 文件

下面这份是我给多数项目起底的模板,去掉了外部数据库和队列,最大程度降低初次部署的复杂度。

services: onlyoffice-documentserver: image: onlyoffice/documentserver:latest container_name: onlyoffice-documentserver restart: unless-stopped ports: - "8080:80" environment: JWT_ENABLED: "true" JWT_SECRET: "replace-with-openssl-rand-hex-32-output" JWT_HEADER: "Authorization" JWT_IN_BODY: "true" ALLOW_PRIVATE_IP_ADDRESS: "true" volumes: - ds_data:/var/www/onlyoffice/Data - ds_log:/var/log/onlyoffice - ds_lib:/var/lib/onlyoffice - ds_db:/var/lib/postgresql - ds_rabbitmq:/var/lib/rabbitmq - ds_redis:/var/lib/redis networks: - onlyoffice_net volumes: ds_data: ds_log: ds_lib: ds_db: ds_rabbitmq: ds_redis: networks: onlyoffice_net: driver: bridge

你不需要真的把这段全部照抄,关键是理解每个字段在干什么。image 决定版本,ports 做端口映射,environment 传鉴权信息,volumes 定义持久化,networks 让容器有独立网络。JWT_SECRET 这一项务必替换成自己的随机字符串,不要用模板里的占位符,否则等于没设密码。

注意:如果你用的是新版 Docker Compose v2,命令是 docker compose(中间有空格),不是老旧的 docker-compose。配置文件里也不需要写 version 字段,Compose 会根据文件内容自动选择语法版本。

3. 部署实操:从下载镜像到启动验证

3.1 环境准备与 Docker Compose 安装

系统里没有 Docker 的话,先装基础运行时。以 Ubuntu/Debian 为例,可以直接用系统包管理器安装 Docker Engine 和 Compose 插件。

sudo apt update sudo apt install -y docker.io docker-compose-plugin sudo systemctl enable --now docker docker compose version

执行完 docker compose version 能看到版本号,说明 Compose 插件已经就位。如果你之前只装了 docker 没装 compose 插件,也可以单独补装。CentOS/RHEL 系列则是用 dnf 装 docker-ce 和 docker-compose-plugin,名字略有差异。

内网离线环境装 Docker 是另一个话题,这里先给一个最实用的思路:在有外网的机器上把镜像打成 tar 包,再导入内网。

docker pull onlyoffice/documentserver:latest docker save onlyoffice/documentserver:latest -o onlyoffice-documentserver.tar

到了内网机器上执行 docker load -i onlyoffice-documentserver.tar,镜像就进本地了。此时再跑 docker compose up -d,Compose 发现本地有同名镜像就不会去远程拉取。

3.2 启动服务与常用管理命令

把上面的 docker-compose.yml 保存到某个目录,比如 /opt/onlyoffice,然后在这个目录里执行:

cd /opt/onlyoffice docker compose up -d

第一次启动会先拉镜像,镜像很大,需要耐心等一会儿。拉完镜像后容器开始内部初始化,这时候你会看到容器状态从 created 变成 starting,最后变成 running。内部要同时拉起好几个服务,初始化时间通常在一到两分钟,不要刚看到容器起来就急着访问。

日常管理最常用的几条命令一起列出来:

# 查看容器状态 docker compose ps # 跟踪日志,排查启动过程中的报错 docker compose logs -f onlyoffice-documentserver # 进入容器排查 docker exec -it onlyoffice-documentserver bash # 重启容器 docker compose restart onlyoffice-documentserver # 停止并删除容器(卷不会被删除) docker compose down

注意 docker compose down 默认只删容器和网络,不会删命名卷,所以数据还在。如果你想连卷一起清掉,要加 -v 参数,但这句话我必须说在前头:加了 -v 等于把 OnlyOffice 的数据库、日志、缓存全删了,非必要不要碰。

3.3 验证部署:健康检查、首页与临时文档

容器起来后别急着开会,先做三个层面的验证。第一层是看进程状态,docker compose ps 里 STATUS 显示 Up 且没有 Restarting 字样,基本说明容器没崩溃。第二层是探测健康检查接口:

curl http://localhost:8080/healthcheck

如果服务正常,这个接口会返回一段文本,常见结果是 true 或类似状态信息。如果返回 404 或者连接拒绝,说明容器内部服务还没就绪或端口映射不对,先看日志。

第三层是确认 API 文件能访问,因为 OnlyOffice 的前端编辑器依赖这段脚本:

curl -I http://localhost:8080/web-apps/apps/api/documents/api.js

HTTP 状态 200 就说明 Web 服务正常。之后可以用浏览器打开主机地址,比如 http://你的服务器IP:8080,看到 OnlyOffice 的欢迎页或编辑器页面。如果要更仔细地验证在线编辑,可以准备一个最简单的 HTML 页面,把 DocsAPI 编辑器嵌进去,指向服务器上的 test.docx,这样能确认 JWT、回调、WebSocket 整条链路是通的。

3.4 常见启动问题与排查技巧实录

部署过程里翻车最多的不是配置复杂,而是基础环境出了问题。我把自己遇到过的几类典型问题整理成一张速查表:

现象排查方向解决办法
容器一直 Restarting,日志里有 OOM内存不够给机器加内存,至少 4GB;或检查是不是同时跑了太多容器
访问 8080 端口 404 / 502容器内部还没完全启动等 1-2 分钟再访问;docker compose logs 看启动进度
8080 端口被占用宿主机上已有服务占端口把映射改成 8081:80,或先停掉占用端口的服务
编辑器加载失败,接口报 401JWT 密钥不一致确认集成方配置的 secret 与容器环境变量 JWT_SECRET 完全一致
转换文档报错,提示无法访问文件容器无法回调内网地址设置 ALLOW_PRIVATE_IP_ADDRESS=true,并检查网络连通性
协同编辑时对方看不到文档刷新WebSocket 没透传反向代理必须支持 Upgrade 和 Connection 头

排查这些问题有个统一的顺序:先 docker compose ps 看存活状态,再 docker compose logs 看最近的错误,最后 curl 健康检查接口看服务是否就绪。大多数“起不来”的问题到这一步就能定位清楚了。

4. 生产环境进阶:HTTPS、Moodle 集成与转换参数

4.1 让域名走 HTTPS:反向代理与 WebSocket 透传

OnlyOffice 文档编辑用了 WebSocket 做协同通信,所以反向代理不能只是简单转发 HTTP,必须额外处理 WebSocket 的 Upgrade 握手。很多人配完 Nginx 后打开页面是好的,但一旦两个人同时编辑,文档状态不同步,十有八九是代理层把 WebSocket 请求当成普通请求转发,导致长连接建立失败。

这里给一份 Nginx 反向代理的关键配置片段:

location / { proxy_pass http://127.0.0.1:8080; 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-Proto $scheme; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; client_max_body_size 100m; }

注意 proxy_set_header Connection 一定要是 upgrade,而且这一行不能与普通的 keepalive 逻辑混掉。X-Forwarded-Proto 也很重要,它告诉 OnlyOffice 用户当前通过 HTTPS 访问,这样内部生成的回调地址、下载链接才不会错误地使用 http。

如果你用 Caddy,配置会简单不少,它默认就会处理 WebSocket 和 HTTPS 证书申请。但生产系统里 Nginx 依然是主流,掌握上面的写法比背工具更重要。

4.2 和 Moodle 集成:装插件与密钥对齐

Moodle 接入 OnlyOffice 的官方路径是安装由 OnlyOffice 提供的 Moodle 插件。插件可以在 Moodle 后台直接上传 Zip 安装:站点管理 -> 插件 -> 安装插件,然后上传下载到的插件压缩包,按提示启用。

装完插件后,进入插件的配置页面,需要填两个最核心的信息:文档服务器地址和 JWT 密钥。文档服务器地址要填完整,比如 https://doc.example.com/,结尾斜杠别漏;JWT 密钥必须和 docker-compose.yml 里的 JWT_SECRET 一模一样。这两处对不上,你在 Moodle 里打开附件时会一直转圈或者直接报错。

比较隐蔽的一个问题是:Moodle 所在的服务器必须能访问到 OnlyOffice 容器,最好在同一网络或者通过防火墙放行。如果 Moodle 和 OnlyOffice 都在 Docker 里,推荐让两个容器加入同一个外部网络,这样 Moodle 可以用容器名解析 OnlyOffice。

提示:集成完以后先上传一个小文件试一下“在线编辑”和“保存回传”。OnlyOffice 编辑完会回调 Moodle 保存文件,如果回调地址被安全策略拦截,版本历史和编辑保存都会失效。

4.3 转换请求里的 assemblyformatasorigin=true 到底是干嘛的

如果你对接过 OnlyOffice 的转换接口,大概率见过某个 URL 参数叫 assemblyformatasorigin=true。这个参数不常出现在文档里,但真遇到格式错乱的问题时,它往往是关键。

我个人的理解和使用经验是:它告诉 OnlyOffice,请以文件的原始装配格式作为转换基准,不要只凭 URL 后缀或者外部传入的 filetype 做判断。举个例子,有些系统导出的文件后缀是 .doc,但内部实际是 docx 的 Open XML 结构;不带这个参数时,转换服务可能按老二进制格式去解析,结果导出 PDF 后排版错乱、中文乱码。加上 assemblyformatasorigin=true,服务端会先尝试识别文件真实的内部格式,再决定转换路径。

在实际对接时,你可能不是在页面上直接改这个参数,而是在集成代码的转换请求 URL 里把它拼接进去。比如:

https://doc.example.com/ConvertService.ashx?url=...#&outputtype=pdf&assemblyformatasorigin=true

如果你当前没有遇到转换异常,不需要刻意加。但把这条经验记在脑子里,等将来有人反馈“某个 doc 转出来是乱的”,排查方向就会清晰很多。

4.4 协同编辑与版本历史:开启要做什么

OnlyOffice 的协同编辑本身不需要额外配置,容器起来就能用,但要让多人编辑实时同步,整个链路必须满足三个条件。第一,浏览器能通过 WebSocket 连接到文档服务,所以反向代理要处理好 Upgrade;第二,所有客户端和回调请求使用同一个 JWT 密钥,签名不一致的请求会被拒绝;第三,文档的保存回调要能回到集成方,否则只在 OnlyOffice 内部改了,业务系统里的源文件不会更新。

版本历史在 OnlyOffice 编辑器界面的“版本历史”入口能看到,每次保存会生成一个新版本。和“代码查看历史修改记录”一样,你可以比较当前版本和之前版本的差异。这个功能依赖于 PostgreSQL 卷,因为版本元数据存在数据库里。

如果你发现版本历史里只有一条记录,或者改动记录无故消失,先检查两件事:数据库卷有没有正常挂载,以及保存回调有没有失败。很多容器重建后用了一个新的匿名卷,历史数据就“丢”了,其实旧数据还在原来的卷里,只是没挂回来。

5. 避坑指南与实用建议

5.1 镜像拉取慢或拉不下来怎么办

OnlyOffice 镜像体积大,拉取慢是个很实际的问题。最稳的办法是给 Docker daemon 配置 registry mirror,也就是镜像加速器。修改 /etc/docker/daemon.json,加入 registry-mirrors 配置项,然后重启 Docker。

{ "registry-mirrors": ["https://你的镜像加速地址"] }

配置完记得 docker info 查看 Registry Mirrors 是否生效。如果你使用的加速地址不可用,就换一个,或者干脆走离线导入路线。还有一个小技巧:不要在高峰期反复拉同一个镜像,第一次拉失败产生的残层会占用磁盘空间,定期 docker system prune 清一下,避免磁盘被占满。

5.2 备份与升级策略

只要容器正常挂着卷,备份并不复杂。最简单的方式是用一个临时容器挂载 OnlyOffice 容器的所有卷,打包整个数据目录。

docker run --rm --volumes-from onlyoffice-documentserver \ -v $(pwd):/backup alpine \ tar czf /backup/onlyoffice-backup.tar.gz \ /var/www/onlyoffice/Data /var/lib/postgresql /var/lib/redis \ /var/lib/rabbitmq /var/lib/onlyoffice /var/log/onlyoffice

这个命令会把关键数据全部打包到当前目录的 onlyoffice-backup.tar.gz。恢复的时候,先把新容器停掉,再用同样的方式把 tar 解包回对应目录即可。备份频率取决于你们的业务量,我的建议是每次升级前必须备份,日常每天或每周定时跑一次。

升级 OnlyOffice 时不要直接 docker compose pull 然后 up -d 就完事。先看官方发布说明,确认新版本是否包含数据库迁移或配置变更,再按“备份 -> 修改 yaml 里的镜像 tag -> docker compose pull -> docker compose up -d”的顺序操作。升级失败就快速把 tag 回滚到旧版本,重新 up -d,所以固定 tag 的好处在这时候体现得淋漓尽致。

5.3 我给新手的几个操作建议

不要一上来就用 latest 跑生产,也不要一开始就拆外部数据库,更不要图省事把 JWT 关掉。关掉 JWT 会让整个服务处于裸奔状态,任何人只要能访问到端口,就可以调用转换接口、伪造回调,这个风险完全不值得冒。

部署时建议把敏感配置抽到 .env 文件里,在 docker-compose.yml 里用 ${JWT_SECRET} 引用。这样 yml 本身可以提交到 Git,真正的密钥留在服务器本地。我第一次部署就是直接把密钥写死在 yml 里,结果同事把仓库同步到自己的开发机,密钥也一起带走了,后来只能重新生成并同步所有集成方,教训很深。

最后再分享一个小习惯:每次改动 compose 文件或者升级镜像之前,先看一眼当前容器是否能正常出健康检查,再备份数据。看起来是土办法,但我在实际项目里靠这个习惯避免了至少两次“升级一时爽,回滚火葬场”的场面。Docker Compose 给了我们一键部署的能力,但真正让服务稳定跑下去的,永远是那些不起眼的备份和验证动作。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/26 11:55:47

MCP安全核心风险:命令注入原理、攻击链与防御落地清单

1. MCP安全头号威胁:命令注入到底是什么?1.1 MCP让AI第一次真正握住了“扳手”MCP(Model Context Protocol,模型上下文协议)这两年的热度,做技术的人应该都有体感。以前AI模型只能生成文字、回答问题&#…

作者头像 李华
网站建设 2026/9/26 11:54:36

AI扫描后端代码生成接口契约,终结Mock联调之痛

干前端这些年,我最怕的一个词不是需求变更,而是 Mock。你可能要说:Mock 不是提升开发效率的好东西吗?前期确实好用,但走到联调那天你就明白了——前端页面里密密麻麻的属性,和后端真正返回的 JSON 对不上号…

作者头像 李华
网站建设 2026/9/26 11:54:31

cdp.dll丢失怎么办?安全修复与免费下载方法详解

"cdp.dll文件丢失找不到问题 免费下载方法分享" 1. cdp.dll是什么:先搞懂你在找的这个文件 最近不少人遇到同一种弹窗:打开某个软件或者开机的时候,Windows直接提示"找不到cdp.dll"或者"由于找不到cdp.dll&#x…

作者头像 李华
网站建设 2026/9/26 11:54:26

工程机械液压传感器MSG玻璃微熔技术:从工艺细节到主机厂供应链切入的实操经验

1. 工程机械液压传感器的行业变局与MSG玻璃微熔的切入逻辑干了十几年传感器这行,我亲眼看着工程机械液压传感器的市场从“能用就行”一路卷到“毫厘必争”。早些年主机厂选型,国产传感器基本是备胎中的备胎,核心液压回路上的压力检测几乎被几…

作者头像 李华