Docker部署ONLYOFFICE文档服务:Nginx反代与HTTPS配置全攻略
1. 为什么选Docker部署ONLYOFFICE而不是裸机装1.1 官方镜像到底装了什么一个容器等于一整套服务我第一次接触ONLYOFFICE时下意识以为它和LibreOffice差不多装个依赖、跑个服务、配置一下就能用。真正上手才发现ONLYOFFICE Document Server是一整套文档处理服务里面有文档转换引擎、在线编辑回调、协同编辑会话管理背后还依赖PostgreSQL、RabbitMQ、Redis这些组件。如果走传统裸机安装光是把这些依赖版本对齐、配置不冲突、开机自启全搞定够你折腾一整天。用Docker部署就是另一回事了。官方维护的onlyoffice/documentserver镜像把所有运行时都打包好了容器内部自带了Nginx、PostgreSQL、RabbitMQ、Redis和文档服务本体。你只需要一条docker run命令拉起来就能跑。这背后的逻辑说得直白点官方替你解决了“组件之间怎么配合”的问题你只需要关心“我该把哪些数据持久化、端口怎么映射、证书放哪里”。我之所以坚持用Docker还有一个很实际的原因——升级和回滚。ONLYOFFICE社区版更新频率不低裸机升级要备份数据、停服务、替换包、重启一堆组件中间任何一个环节出错在线编辑功能就挂了。容器方式只需要换镜像Tag再重建容器数据卷不动升级风险小很多。万一新版本有问题切回旧镜像一条命令恢复原样。1.2 容器版本和裸机版本怎么选先看一张我自己整理的对比表方便你判断自己场景适合哪种方式对比项Docker部署裸机/虚拟机部署部署耗时10分钟左右取决于拉镜像速度至少半天踩坑可能要一两天组件依赖容器内置自动管理需手动安装Nginx/PG/RabbitMQ/Redis升级回滚改Tag重建容器分钟级完成手动备份替换包链路长资源占用镜像分层实际内存占用略高相对更节约但差异不大与宿主耦合低日志和数据目录挂载出来后很清晰高环境变动可能影响服务适合场景中小团队、快速上线、需要频繁升级对资源敏感、已有完整运维体系的大厂如果你的服务器内存小于4GB我建议慎重考虑ONLYOFFICE。这个服务本身很吃内存容器跑起来基线就要1.5GB到2GB左右如果再塞进Java后端、MySQL等业务服务很容易触发OOM。文档转换和大文档并发编辑时内存还会继续涨。内存再少哪怕是部署成功了在线编辑体验也会卡到怀疑人生。我在公司内部部署时给这台服务单独分配的配置是4核8GB同时跑ONLYOFFICE容器和Nginx反代实测连续10个人同时在线编辑、频繁保存修改内存使用率稳定在70%以下没有发生过OOM。2. 部署前的环境评估与关键参数准备2.1 域名和证书方案免费证书、自签证书、商业证书怎么选ONLYOFFICE的HTTPS方案有个特点它不是“部署完了能打开网页”就行还要考虑编辑器保存时服务端之间的回调。你的业务系统比如Java后端会通过HTTPS回调ONLYOFFICE服务如果证书不被系统信任回调就会失败表现症状就是“编辑器打不开”、“保存报错”、“一直加载中”。所以证书选型很关键。按我实际经验优先级如下第一选择Lets Encrypt免费证书。如果你有公网域名直接申请Lets Encrypt90天有效期配合certbot自动续期经过实际验证最省心、兼容性最好浏览器和服务端两边都认。第二选择自签CA证书并加入信任链。如果你在内网环境没有公网域名也没法通往外网那只能自签。但要注意自签证书不能只签一张最好用自建CA签发服务器证书然后让所有调用ONLYOFFICE的客户端和服务端都信任这个CA。后面我会细讲这个操作。第三选择商业证书。适合对证书链完整性和品牌展示有要求的生产环境比如要交付给外部客户使用。商业证书签发流程和Lets Encrypt类似只是要花钱买。不建议直接裸奔用HTTP。一旦你的业务页面是HTTPS浏览器会拦截对HTTP资源的请求ONLYOFFICE编辑器根本加载不出来。所以HTTPS不是可选项是必选项。2.2 服务器、防火墙与Docker环境准备在动手前先把这些准备工作做完省得后面踩坑开放端口至少放行80和443端口。ONLYOFFICE文档服务走的是80端口Nginx反代对外提供443。如果你在内网需要在防火墙上放行如果在云服务器还要去安全组里加规则。我实际遇到过一个情况服务器本地curl没问题外部浏览器就是连不上排查了一圈发现是云安全组忘了放行443。安装Docker和Docker Compose建议直接用官方安装脚本装Docker版本不要太老。Docker Compose看是否单独需要如果用docker compose插件形式直接就能用。国内拉镜像慢的提前在/etc/docker/daemon.json里配置镜像加速地址配置完重启Docker再拉镜像。确认系统时区ONLYOFFICE的日志时间要是差了8小时排查问题时会很痛苦。建议把宿主机和容器都设置成Asia/Shanghai。容器时间可以通过环境变量TZ指定。检查内存和Swap保险起见给这台服务器至少要4GB可用内存。如果内存紧张可以加2GB Swap兜底但Swap不能解决所有问题长时间高负载应用别依赖Swap。2.3 数据卷挂载和JWT参数这些配置要提前想明白ONLYOFFICE容器把数据分成几个目录分别是文档数据、数据库、日志、配置。强烈建议把它们全部持久化到宿主机目录否则容器一删数据全没了。我在生产环境中用的挂载规则如下容器内路径宿主机路径存什么/var/www/onlyoffice/Data/opt/onlyoffice/data文档、证书、字体等用户数据/var/log/onlyoffice/opt/onlyoffice/logs服务日志/var/lib/onlyoffice/opt/onlyoffice/lib扩展和缓存/var/lib/postgresql/opt/onlyoffice/dbPostgreSQL数据文件挂载目录的权限要注意。容器里的进程用户不是root宿主机目录如果是root用户的默认权限容器可能写不进去。我在第一次部署时就直接建目录、默认权限结果PostgreSQL初始化失败容器反复重启。后来把目录属主改成root还不够用chmod -R 777才跑起来。不推荐777放在公网服务器上但内网场景可以接受更稳妥的做法是找到容器里运行用户的UID然后把宿主机目录属主设置成那个UID。还有JWT参数。ONLYOFFICE从7.x版本开始默认启用JWT签名编辑器向文档服务发起请求时都要带token防止被伪造。你的集成方比如Java后端、NextCloud、自定义前端如果没配置同一个JWT_SECRET会出现“文档密钥无效”之类的报错。我建议自己生成一个超过32位的随机字符串保存好后面所有地方都用这个。3. 核心实操Docker部署ONLYOFFICE文档服务3.1 拉取镜像并启动容器完整命令和Compose配置先拉镜像。注意不要用latest生产环境最好锁定一个大版本Tag比如7.5.0等测试无误后再升级。docker pull onlyoffice/documentserver:7.5.0启动方式有两种。不需要编排的服务直接docker run最快docker run -i -t -d -p 80:80 \ --restartalways \ --name onlyoffice \ -v /opt/onlyoffice/data:/var/www/onlyoffice/Data \ -v /opt/onlyoffice/logs:/var/log/onlyoffice \ -v /opt/onlyoffice/lib:/var/lib/onlyoffice \ -v /opt/onlyoffice/db:/var/lib/postgresql \ -e JWT_ENABLEDtrue \ -e JWT_SECRETyour_strong_secret_here_at_least_32_chars \ -e TZAsia/Shanghai \ onlyoffice/documentserver:7.5.0如果你更习惯Compose管理可以这样写version: 3 services: onlyoffice: image: onlyoffice/documentserver:7.5.0 container_name: onlyoffice restart: always ports: - 80:80 environment: - JWT_ENABLEDtrue - JWT_SECRETyour_strong_secret_here_at_least_32_chars - TZAsia/Shanghai volumes: - /opt/onlyoffice/data:/var/www/onlyoffice/Data - /opt/onlyoffice/logs:/var/log/onlyoffice - /opt/onlyoffice/lib:/var/lib/onlyoffice - /opt/onlyoffice/db:/var/lib/postgresql这里解释一下为什么映射的是80:80。ONLYOFFICE容器内部自带了一个Nginx监听80端口。它负责把请求分发到文档服务的各个内部组件。外部想直接用HTTPS访问不能在容器内把443直接映射出去因为容器内没有配置证书。正确做法是让容器继续监听80在外层用Nginx宿主机Nginx或独立Nginx容器接收443流量再反代到容器80端口。这也是官方推荐的做法。启动后先看日志确认有没有报错docker logs -f onlyoffice观察输出里有没有“successful”或者报PostgreSQL、NetCore服务启动失败的日志。首次启动要初始化数据库可能需要一分钟左右别急着下结论。3.2 验证容器是否正常healthcheck和欢迎页容器启动完成后先在本机验证一下。curl -I http://localhost/healthcheck正常情况下会返回HTTP 200响应体是字符串true。这说明文档服务核心进程和数据库连接都是好的。再打开http://你的服务器IP/welcome/能看到ONLYOFFICE的欢迎页面。这个页面本身不能编辑文档但是能验证Nginx路由是否正常。真正测试在线编辑需要通过集成的业务系统发起一个编辑请求。我在这一步遇到过一个问题healthcheck返回200但欢迎页打不开。后来发现是容器内的Nginx配置和Data目录没挂载对部分静态资源读取失败。这种问题要看容器日志才能发现所以我会反复强调任何异常先看docker logs。3.3 踩坑记录为什么容器起来了HTTPS还是访问不了很多新手在这里会疑惑ONLYOFFICE容器已经监听80了浏览器通过http://ip也能打开那HTTPS怎么弄直接在Docker映射443端口行不行先说结论不行。容器内部的Nginx只监听80而且没有任何证书配置。即使你把宿主机的443映射到容器的80也不是HTTPS。浏览器访问https://ip时TLS握手发生在容器外的Nginx或负载均衡器上不是容器内的Nginx。所以一旦你的业务站点升级成了HTTPS浏览器加载http://onlyoffice资源时就会被混合内容策略拦截编辑器白屏、加载不到脚本就是这一步导致的。接下来要做的事情就清晰了在宿主机上装一个Nginx承担TLS终止和反向代理的职责。容器继续留在80端口做人肉转发最省心。4. 打通HTTPS访问链路Nginx反向代理配置详解4.1 宿主机Nginx还是Nginx容器怎么选我推荐直接在宿主机装Nginx不建议再开一个Nginx容器。理由有三点少一个容器就少一层网络通信和资源占用。证书续期的时候Skyler通过宿主机文件直接挂载进去比docker cp进容器再reload简单得多。排错链路更短客户端 - 宿主机Nginx - Docker容器openSSL和curl测试都很方便。除非你有一整套基于容器的统一运维平台否则宿主机Nginx是中小团队最优解。Nginx安装方式不多说Ubuntu/Debian用apt install nginxCentOS用yum install nginx。装完把默认站点禁用新建一个ONLYOFFICE专属配置。4.2 申请Lets Encrypt证书并配置Nginx我的服务器是Ubuntu直接装certbotapt install certbot python3-certbot-nginx -y如果ONLYOFFICE已经在80端口跑起来了先确保有一个域名解析到这台服务器IP然后执行certbot --nginx -d onlyoffice.example.comcertbot会自动修改Nginx配置并配置好证书路径。如果你不想让它自动改Nginx配置也可以用WebRoot方式只签发证书手动写Nginx配置。我更推荐手动方式因为ONLYOFFICE有一些特殊的代理参数certbot自动生成的模板不一定合适。手动配置的核心Nginx站点文件如下server { listen 443 ssl http2; server_name onlyoffice.example.com; ssl_certificate /etc/letsencrypt/live/onlyoffice.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/onlyoffice.example.com/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; # 上传大小限制建议设置大一些否则大文档上传会413 client_max_body_size 100m; # ONLYOFFICE在线编辑是长连接读超时和发送超时都要放长 proxy_read_timeout 3600s; proxy_send_timeout 3600s; location / { proxy_pass http://127.0.0.1:80; 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; } } server { listen 80; server_name onlyoffice.example.com; return 301 https://$host$request_uri; }保存后测试配置并重载Nginxnginx -t systemctl reload nginx然后访问https://onlyoffice.example.com/welcome/浏览器不再报证书错误整个链路就算打通了。注意proxy_set_header X-Forwarded-Proto $scheme这一行很关键。ONLYOFFICE文档服务会通过这个头判断请求是HTTP还是HTTPS然后在回调URL、资源引用地址里生成对应的协议。如果漏掉这个头即使你浏览器通过HTTPS访问文档服务生成的内部URL仍可能是HTTP编辑器照样加载不全。4.3 WebSocket和代理缓冲这些参数为什么不能省ONLYOFFICE在线编辑过程中协同编辑和文档变化的推送依赖WebSocket。Nginx反代WebSocket需要特殊处理就是配置里那两行proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade;没有这两行你打开编辑器能加载文档但多人协同编辑时别人输入的字符不会实时同步还会出现“连接已断开”的提示。这个问题比较隐蔽不排查很难发现。还有代理缓冲。Nginx默认会缓冲后端响应ONLYOFFICE有些实时交互接口需要即时返回数据如果缓冲导致延迟过大编辑器会表现得很“卡顿”。我通常会在location里加上proxy_buffering off; proxy_cache off;实测下来这两个配置对首屏加载速度和保存响应体感有提升。4.4 证书自动续期避免90天后访问突然挂掉Lets Encrypt证书有效期只有90天必须配置自动续期。certbot提供了一个--renew-hook参数在续期成功后重载Nginx让新证书生效。certbot renew --renew-hook systemctl reload nginx用crontab每两天执行一次检查0 3 */2 * * /usr/bin/certbot renew --renew-hook systemctl reload nginx /var/log/letsencrypt/renew.log 21续期操作本身不会中断ONLYOFFICE服务因为只是把证书文件替换掉Nginx reload是平滑重载不丢连接。5. 常见问题与排查技巧实录5.1 编辑器一直加载/文档服务返回错误先怀疑回调链路在线编辑架构里你的业务服务器和ONLYOFFICE文档服务器之间是“服务端到服务端”的通信。最常见的一个坑业务服务器访问ONLYOFFICE时用的还是自签证书或内网IP导致TLS校验失败。我处理过一个典型case业务站点是https://oa.example.comONLYOFFICE部署在内网http://192.168.1.100业务页面能正常打开编辑器框架但一发起编辑请求就报“文档服务返回错误”。查下来发现业务后端回调ONLYOFFICE的地址是http但浏览器里的混合内容拦截了该HTTP地址。解决办法给ONLYOFFICE配上HTTPS域名业务后端回调地址改为https://onlyoffice.example.com两边全是HTTPS问题立刻消失。处理这种问题我习惯分三步排查在服务器上用curl -I http://localhost/healthcheck确认文档服务存活。用curl -I https://onlyoffice.example.com/healthcheck确认外部HTTPS链路正常。用curl -vk https://onlyoffice.example.com检查证书链是否完整结合-k只是跳过验证看返回。如果第二步和第三步失败问题出在Nginx或证书如果都通过问题多半出在业务系统的回调地址或JWT配置。5.2 浏览器内网IP访问HTTPS证书警告无法消除这个问题的根源是证书和访问地址不匹配。Lets Encrypt证书只能签域名不能签IP地址除非是公有IP且有特殊验证。你在内网用https://192.168.1.100访问浏览器会报“证书名称不匹配”即使证书部署正确也一样。解决办法有两个一是内网DNS加域名解析把onlyoffice.example.com解析到内网IP然后用域名访问信任Lets Encrypt根证书提示自然消失。强烈推荐这种方式。二是自签CA。如果没有内网DNS就自己建CA签发一个带内网IP或内网域名的证书再把这个CA导入所有客户端的信任列表。这套操作对服务器端也要做一次因为服务端之间调用的时候也需要信任这个自签CA。5.3 Java后端调用ONLYOFFICE时证书校验不过怎么解决Java程序默认信任JDK的cacerts证书库。如果ONLYOFFICE用的是自签证书Java会报PKIX path building failed。解决思路有两个把自签CA证书导入Java的cacerts证书库keytool -import -trustcacerts -alias onlyoffice-ca -file ca.crt -keystore cacerts或者在后端代码里跳过SSL验证。这个方法测试环境应急可以生产环境一定要用方案一跳过验证等于把安全守门员撤掉了风险太高。5.4 在线编辑保存报错看ONLYOFFICE容器日志找真相保存失败这个问题经常被表象带偏。有一次用户反馈总是在编辑后点击保存时报错但有时能保存成功。我一开始怀疑是内存不足看了系统指标很正常。后来进容器看日志docker logs --tail 200 onlyoffice发现大量RabbitMQ connection is not open之类的连接错误。查了一下容器里的RabbitMQ和ONLYOFFICE服务之间有内部网络通信由于我宿主机做了端口映射冲突导致RabbitMQ初始化异常。排查后调整端口映射问题解决。所以遇到保存失败、连接断开这类问题第一件事永远是看容器日志别凭感觉改配置。ONLYOFFICE日志文件在挂载的/opt/onlyoffice/logs目录下结合时间点查看docservice和converter相关日志最有用。5.5 容器反复重启多半是数据卷权限或内存不足ONLYOFFICE容器启动失败、反复重启最常见的原因就是挂载目录权限不对。容器里的PostgreSQL进程以postgres用户身份跑如果你宿主机挂载的目录权限不允许它写数据库初始化就会失败。这时候用docker logs能看到清晰的权限报错。解决办法先停容器把数据库目录的属主改成容器内PostgreSQL的UID。查找UID用这条命令docker run --rm onlyoffice/documentserver:7.5.0 id postgres一般得到的UID是999然后把宿主机数据库目录属主改成这个chown -R 999:999 /opt/onlyoffice/db改完再启动容器基本就好了。还有内存不足的问题。ONLYOFFICE社区版对内存管理不吝啬单文档转换进程可能吃掉几百MB。系统内存不够时会触发内存回收甚至OOM Killer杀掉容器进程表现就是容器重启。最简单有效的调优方案是把文档转换并发数上限降低修改容器内配置文件/etc/onlyoffice/documentserver/local.json里的converter\.converter\.concurrency调成2或4降低峰值内存压力。5.6 修改local.json配置后不生效记得重建容器ONLYOFFICE的配置修改后需要在容器内重启文档服务相关进程才会生效。单纯改宿主机目录里的文件不会立刻加载。常见的做法docker exec -it onlyoffice supervisorctl restart all如果你的配置始终不生效可以检查一下挂载映射是否正确。我曾经因为把/opt/onlyoffice/lib挂载到了容器里但local.json实际路径在/etc/onlyoffice所以改挂载目录里的文件根本没用。后来把/etc/onlyoffice单独挂载出来才实现了配置持久化。注意ONLYOFFICE镜像里的/etc/onlyoffice目录在多版本镜像里路径是相对稳定的但官方升级有可能会改默认配置。生产环境我建议都用local.json覆盖层不要直接改全局配置文件升级时冲突少。6. 在线编辑集成时几个容易忽略的隐蔽细节6.1 业务系统是HTTPSONLYOFFICE也必须是HTTPS很多团队部署ONLYOFFICE时业务系统已经跑在HTTPS。浏览器加载在线编辑器时嵌入页面的方式通常是iframe或JS调用。如果父页面是HTTPSiframe里的源是HTTP浏览器会直接拦截白屏问题就是这么来的。所以集成ONLYOFFICE前先确认自己的业务页面协议和ONLYOFFICE访问协议保持一致。HTTPS页面就配HTTPS的ONLYOFFICE这在架构设计阶段就要定下来后期再改证书和回调地址很麻烦。6.2 JWT密钥不一致编辑器报“invalid token”ONLYOFFICE 7.x之后文档服务默认开启JWT。如果你在容器启动时只设置了JWT_ENABLEDtrue没有设置固定的JWT_SECRET容器会随机生成一个密钥你业务系统那边根本不知道这个密钥所有来自集成方的请求都会被判定为无效。启动时必须手动指定-e JWT_SECRETyour_strong_secret_here然后把这个密钥同步到业务系统的配置里。Java集成时常见的是在初始化连接器时传入jwtSecret参数。两边不一致就会出现一个很经典的报错JWT verification failed或Invalid token。网上有些旧教程让你把JWT_ENABLED改成false来绕过。这可以做但我不建议在生产环境这么做因为它会让未授权的人绕过校验直接调用文档服务接口等于把文档编辑入口裸奔在公网上。安全无小事多花一分钟配置密钥省的是后面出事的麻烦。6.3 服务端之间回调地址不能写localhost这个坑特别隐蔽。如果你在服务器上同时部署了ONLYOFFICE容器和业务系统容器并在业务系统里把ONLYOFFICE地址配置成了http://localhost或http://127.0.0.1看起来能通但浏览器端实际加载编辑器时你的用户浏览器解析不到这个localhost。正确的做法是填外部可访问的地址比如https://onlyoffice.example.com。不仅用户浏览器能访问业务服务器也能访问。这样才能保证前端编辑和后端回调走同一条链路。6.4 使用强制HTTPS跳转后ONLYOFFICE的WebSocket也要跟着走HTTPS如果你在Nginx里配置了80强制跳转443ONLYOFFICE的WebSocket连接也必须走wss://。Nginx配置里带上Upgrade头即可大多数情况下它会自动跟随X-Forwarded-Proto生成wss。如果发现编辑器能打开但实时协同失效用开发者工具看WebSocket连接状态如果显示ws://多半是Nginx头配置没生效。6.5 文档转换和预览为什么慢先检查字体缓存ONLYOFFICE转换文档依赖中文字体如果容器里没有中文字体转换出的PDF会出现乱码甚至转换失败。部署时建议把宿主机常见字体挂载进容器或者复制到Data目录下的字体目录。我在生产环境做法是cp -r /usr/share/fonts/truetype/* /opt/onlyoffice/data/fonts/然后重启容器。这个操作很小的但对中文文档体验提升非常明显。最后再分享一个小技巧在实际部署ONLYOFFICE的时候我习惯把所有配置和挂载统一整理成一个Shell脚本或Compose文件放在/opt/onlyoffice目录里。这样不管是审计、迁移还是重新部署一条命令就能恢复整套服务。容器部署最大的价值在于“可复现”如果你只是手动敲命令配好就跑那这套环境在三个月后可能连你自己都说不清楚是怎么搭出来的。把所有操作变成代码和配置才是Docker真正改变运维方式的地方。ONLYOFFICE本身很成熟配合上HTTPS访问更像是一个正规生产系统的样子这套方案我在多个项目里实际跑过稳定可靠希望这篇文章能让你少走几个我已经踩过的坑。