首页 / 资讯中心 / 文章详情

Overleaf 本地私有部署实战:Docker Compose 搭建 LaTeX 协作平台

Overleaf 本地私有部署实战:Docker Compose 搭建 LaTeX 协作平台 ★ FEATURED ARTICLE
平时写论文的人多多少少都跟 Overleaf 打过交道。在线版开箱即用几个朋友拉个链接就能共同改一篇稿子确实方便。但真到了要稳定产出的时候在线版的几个问题就很磨人网络波动导致保存失败、编译排队、私有模板没法直接传、项目一多免费套餐根本不够用。我自己项目里有几十个模板还要跟同事内部协作数据放别人服务器上心里始终不踏实后来干脆在本地服务器上自己部署了一套 Overleaf。这篇文章会把本地部署 Overleaf 的完整过程讲透包括为什么选 Docker Compose 而不是源码安装、镜像到底该用哪个、环境变量怎么配、编译容器和中文字体怎么处理、数据怎么备份、最常见的坑有哪些。适合手里有一台 Linux 服务器哪怕是 4 核 8G 的旧机器的人参考也适合实验室、小团队想搭一个内网私有 LaTeX 协作服务的情况。已经用过在线版、懂一点 Docker 基本概念的话照着下面的步骤能少走不少弯路。1. 为什么需要本地部署在线服务的痛点和自建的收益1.1 典型使用场景与需求分析先说说我自己的使用场景。我做科研写作手里的项目文件动辄几十 MB图片多、参考文献多还要用一些自定义的文档类和脚本。在线版免费账号最多能建的项目有限编译时间限制也比较紧稍微大一点的文档就容易编译超时。最烦的是如果正在开会或者在外地网络一抖改到一半的内容可能就同步不上去了。另一个需求是团队协作。我们小组内部经常需要互相评阅论文草稿用微信传文件版本乱用在线文档又不支持 LaTeX。搭一个内网 Overleaf 之后每个人用自己的账号登录项目互相共享评论区标注意见改动记录自动保存一下解决了版本管理的痛点。还有一层考虑是数据合规和隐私。有些投稿之前的稿子、审稿意见相关的回复文件内部并不想放到公共云端服务上。自建服务部署在公司或实验室的内网环境里数据只走内部网络权限自己控制安全性高得多。1.2 本地部署方案的选型逻辑Overleaf 的社区版是开源的官方也提供了 Docker 镜像和一套基于 Toolkit 的部署脚本。实际用起来我发现直接抄官方 Toolkit 虽然省事但对依赖性很强升级路径也封闭。我更推荐自己写 docker-compose.yml把 MongoDB、Redis、Overleaf 主容器、TeX Live 编译容器这几个组件单独管理起来这样每个组件都能按需替换升级也清楚。选 Docker Compose 而不是裸装源码理由很直接。Overleaf 社区版本身由十几个子服务组成比如 Web 前端、文档更新器、编译队列、CLSI 编译接口等等源码部署需要逐一配置 Node 环境和进程管理工作量很大。而 Docker 镜像把主程序打包好了外部只需要 MongoDB 和 Redis 两个依赖。用 Compose 管理一条命令就能拉起整个栈排查问题的时候也能单独看某个容器的日志维护体验好得多。另外就是镜像选择。早期 Overleaf 社区版叫 ShareLaTeX镜像名是sharelatex/sharelatex。后来合并成 Overleaf 之后镜像名变成了overleaf/overleaf。这两个镜像在功能上有延续性但新版本修复了不少安全问题和编译性能问题所以部署的时候别用老版本的教学文档尽量用新镜像。社区版虽然比企业版少一些功能比如高级权限管理、自定义认证对接但单机和中小团队完全够用。2. 部署前的准备工作关键概念与架构梳理2.1 Overleaf 本地版的核心架构Overleaf 本身并不是一个单体应用而是一组协同工作的服务。理解了架构后面配环境变量就不容易懵。主容器里运行的是 Overleaf 的各个 Node 服务包括 Web 服务、项目管理、实时协同编辑、文档更新、编译调度等。当你打开一个项目浏览器通过 WebSocket 跟文档更新器保持连接光标移动和内容变更会实时同步。点击编译时主容器会把编译请求丢给一个队列然后由 CLSI 服务调度 TeX Live 容器去执行实际的编译。编译容器是独立存在的跑的是完整的 TeX Live 发行版。这样做的好处是即使编译任务把 CPU 吃满也不会拖垮主应用的 Web 服务编译过程中产生的临时文件、生成的 PDF 都隔离在单独的容器文件系统里。理解了这一点你就知道为什么部署时要单独指定SHARELATEX_TEXLIVE_IMAGE因为编译容器是一个独立镜像。MongoDB 存的是用户账号、项目元数据、协作历史记录、评论、权限等。Redis 则作为缓存和队列的消息中间件。项目正文文件本身.tex、图片、参考文献通常存在主容器挂载出来的数据目录里文档更新器会负责把这些文件同步到编译容器的工作目录中。2.2 硬件与软件环境清单先给一份我自己实测下来比较舒适的硬件参考CPU4 核以上。编译 LaTeX 是单线程为主核心多了主要是为了同时编译多个项目不卡。内存主容器建议至少 4GB加上 MongoDB 和 Redis整机 8GB 起步。项目里有大型 TikZ 图或者大量图片的16GB 更稳。磁盘100GB 起步。Overleaf 数据目录里会保存所有项目文件和历史版本TeX Live 镜像本身也有几个 GB。操作系统Debian/Ubuntu/CentOS 都行基本要求是 Docker 20.10、docker-compose 1.29 或 Docker Compose v2。Docker 版本不要图新但也不能太老。我踩过一个坑在老旧 Docker 版本上容器内用户权限映射不对导致 Overleaf 往数据目录写文件时出现 Permission Denied后来升级 Docker 就好了。提示如果你是要给团队用不要让 Overleaf 跑在笔记本上。至少放到一台 7×24 小时的服务器上否则别人半夜编译一个长文档你的电脑一关机整个服务就没了。2.3 镜像选型与版本选择我部署的时候用的镜像组合如下overleaf/overleaf:latestmongo:4.4redis:6.2texlive/texlive:latest这里有一个关键点docker-compose 里用latest标签在初期方便但正式使用后要固定 tag。因为 Overleaf 社区版更新频率并不慢有时候大版本升级会引入数据库结构变化自动拉到新版本可能造成前后端不兼容。我建议部署时看一次镜像的发布时间和 GitHub Releases定下一个稳定的 tag比如overleaf/overleaf:5.0之类的版本号升级时再主动换 tag 并测试。mongo:4.4这个选择要单独说明。Overleaf 社区版官方文档里长期使用 MongoDB 4.4 系列。别急着上 MongoDB 6.x因为 Overleaf 内部某些查询逻辑和历史记录存储结构是围绕 4.4 的行为设计的盲目升级容易碰到兼容性问题。texlive/texlive:latest是官方维护的 TeX Live 镜像包含完整宏包和字体。但这里建议看清楚 tag 体系。如果你有特定的中文排版需求或者想自己往镜像里加字体可以基于texlive/texlive写一个 Dockerfile 扩展然后把这个自定义镜像填到SHARELATEX_TEXLIVE_IMAGE环境变量里。3. docker-compose 配置与目录规划3.1 目录规划和数据持久化设计我在服务器上用的是/opt/overleaf作为根目录里面结构如下/opt/overleaf ├── docker-compose.yml ├── .env ├── sharelatex_data/ # Overleaf 项目数据和上传文件 ├── mongo_data/ # MongoDB 数据文件 ├── redis_data/ # Redis 持久化文件 └── texlive/ # 自定义 TeX Live 镜像构建文件这四块地方各有各的作用。sharelatex_data是最核心的里面保存着所有项目源文件、上传的图片、生成的 PDF、历史版本快照。mongo_data存的是数据库文件丢了它用户账号和项目元数据就没了。redis_data虽然主要是缓存和队列但 Overleaf 有些会话相关数据也写在 Redis最好也做持久化。给文件和目录设置权限的时候要注意容器里的进程通常以node用户运行宿主机的目录需要允许写。我第一次部署直接用chmod -R 777图省事虽然能用但不太安全。后来改为把目录所属用户设置成与容器内 UID 一致再配合 bind mount这样既安全又不容易出现权限问题。3.2 环境变量详解从端口到编译超时docker-compose.yml 的核心部分我直接贴在下面这是我自己调过很多次的版本version: 3.8 services: mongo: image: mongo:4.4 container_name: overleaf_mongo restart: unless-stopped volumes: - ./mongo_data:/data/db networks: - overleaf-net redis: image: redis:6.2 container_name: overleaf_redis restart: unless-stopped command: redis-server --appendonly yes volumes: - ./redis_data:/data networks: - overleaf-net sharelatex: image: overleaf/overleaf:latest container_name: overleaf_main restart: unless-stopped depends_on: - mongo - redis ports: - 80:80 environment: SHARELATEX_APP_NAME: 内部 LaTeX 协作平台 SHARELATEX_SITE_URL: http://overleaf.example.com SHARELATEX_MONGO_URL: mongodb://mongo/sharelatex SHARELATEX_REDIS_HOST: redis SHARELATEX_TEXLIVE_IMAGE: texlive/texlive:latest SHARELATEX_SECURE_COOKIE: false SHARELATEX_SESSION_SECRET: 换成你自己的随机长字符串 SHARELATEX_EMAIL_ACCOUNT: SHARELATEX_EMAIL_DISABLE: true volumes: - ./sharelatex_data:/var/lib/sharelatex - ./sharelatex_data/data:/data networks: - overleaf-net networks: overleaf-net: driver: bridge这几个环境变量是必须搞清楚的。SHARELATEX_SITE_URL决定了页面里各种链接的生成如果你用 IP 访问就填http://你的IP用域名访问就填完整的域名否则登录、邀请链接、编译结果下载都可能出现回调地址错误。SHARELATEX_MONGO_URL必须用 Compose 网络里的服务名mongo不要写localhost。SHARELATEX_SECURE_COOKIE在纯 HTTP 内网场景下必须设为false。我之前配了域名但还没上 HTTPS 时把它保留成true结果登录后 Cookie 一直种不上不断跳回登录页排查了半天才发现是这个参数。重要SHARELATEX_SESSION_SECRET一定要设置成随机字符串别用默认值。它是用来签写会话 Cookie 的用了固定默认值以后同一网络里别人也能伪造会话属于很基础但容易遗漏的安全配置。除了主容器编译相关还有一个超时配置。默认编译超时对长文档比较紧可以在环境变量里加上COMPILE_TIMEOUT单位毫秒。我一般调到 180000也就是 3 分钟。注意这个值不要设得太夸张因为如果编译卡死超时机制是最后的兜底太长会把编译队列堵住所有人都跟着变慢。3.3 备份与恢复方案数据备份这件事我是在第二天发现服务器磁盘一块盘掉线之后才认真做的。分享一个我现在在用的方案。MongoDB 的备份用官方mongodump工具因为是容器化部署不需要在宿主机装 MongoDB 客户端直接起一个一次性容器连到 Mongo 服务上导出即可docker run --rm --network overleaf-net \ -v /opt/backups:/backup \ mongo:4.4 \ mongodump --host mongo --db sharelatex \ --archive/backup/mongo-$(date %Y%m%d).archive项目文件备份更简单直接打包sharelatex_data目录即可。建议把两个备份操作写在同一个脚本里每天凌晨执行保留最近 30 天的备份文件。恢复时大致就是反过来把项目文件解压回原目录用mongorestore把数据库导入。恢复流程一定要在没正式用起来的测试环境演练过一次不然真出事的时候手忙脚乱。4. 编译环境的调优与中文字体处理4.1 编译容器的工作原理Overleaf 的编译流程不是主容器自己跑模板引擎而是把.tex项目文件丢给独立的 TeX Live 容器执行。这意味着你往 Overleaf 里上传的.sty、.cls文件以及项目里引用的图片都会先同步到编译容器的工作目录然后由 latexmk 调用相应引擎pdflatex、xelatex、lualatex完成编译。所以要调优编译环境核心就是打造一个合适的 TeX Live 镜像。默认的texlive/texlive:latest宏包很全中文支持也算不错但有些细节需要处理。比如默认镜像里中文字体用的是 Noto CJK如果你需要宋体、黑体这些特定字体就得自己加。还有一种情况项目用到了某个比较冷门的宏包默认镜像里没有编译时直接报! LaTeX Error: File xxx.sty not found.这时候有两种方案一种是在项目里放.sty文件另一种是把宏包固化到镜像里。我给团队的做法是写一个扩展 Dockerfile把常用宏包和字体一次性打好FROM texlive/texlive:latest RUN tlmgr install cjk cjkutils luatexja ctex \ xecjk fandol-fonts RUN apt-get update \ apt-get install -y fonts-noto-cjk fonts-noto-cjk-extra \ rm -rf /var/lib/apt/lists/*构建完成后把镜像推到内网 registry然后在 docker-compose.yml 里把SHARELATEX_TEXLIVE_IMAGE改成自定义镜像名。这样每次编译都是同一个环境不会因为容器重建而出现宏包丢失。提示texlive/texlive镜像的latesttag 和 Overleaf 官方推荐的 tag 不一定对应。All Overleaf 版本对 latexmk 的版本敏感如果编译报错提示 latexmk 版本太老或者行为不一致优先检查SHARELATEX_TEXLIVE_IMAGE指向的镜像时间戳。4.2 中文字体与额外宏包的安装中文支持是本地部署里被问得最多的问题。实际上 Overleaf 在线版默认对中文支持还可以但自建后很多人发现中文文档编译出来乱码或者缺少字体。原因有两个一是文档没指定正确的编译引擎二是字体缺失。我建议中文写作统一用 XeLaTeX 引擎文档里这样写\documentclass[12pt]{article} \usepackage{ctex} \setCJKmainfont{Noto Serif CJK SC} \begin{document} 你好世界。 \end{document}编译引擎选择在 Overleaf 里通过左上角的菜单切换或者在项目根目录放一个latexmkrc文件$pdf_mode 4; # 使用 xelatex $main_file main.tex;这里的$pdf_mode 4表示用 xelatex 编译并直接生成 PDF。如果项目里有多个.tex根文件把$main_file也写进latexmkrc里能避免编译系统进错根文件。关于字体版权我再多说一句。网上很多人直接把 Windows 的宋体、黑体文件拷进容器用如果只是内部协作问题不大但如果是对外发表或产品内分发字体版权有风险。更稳妥的做法是用 Noto CJK 系列字体开源免费且覆盖中日韩字符印刷效果不差。5. 常见问题与排查技巧实录5.1 容器起不来端口、网络与依赖顺序部署中最常见的问题是docker-compose up -d之后sharelatex 容器反复重启。排查第一步永远看日志docker logs --tail 200 overleaf_main如果日志一直提示连接 MongoDB 失败先确认 Mongo 容器是否健康docker ps docker exec -it overleaf_mongo mongosh --eval db.runCommand({ping:1})MongoDB 容器启动速度比主容器慢第一次部署时depends_on只是保证启动顺序没法保证 Mongo 内部初始化完成。我一般会在docker-compose.yml里给 sharelatex 加一个健康检查等待逻辑或者简单粗暴一点主容器重启策略设为unless-stopped等 30 秒后它会自己连接上。端口冲突也是高频错误。默认映射80:80如果宿主机 80 端口被 nginx 或者其他 Web 服务占了容器会一直port is already allocated。解决办法是换成别的宿主端口比如8080:80或者先把 nginx 停掉。5.2 编译超时和编译队列卡死本地部署最常见的性能问题是编译超时。尤其是有大量 TikZ 图表的项目第一次编译可能要好几分钟。我分别遇到过两类现象一类是单个项目编译过慢一直转圈。这种属于单任务耗时超过COMPILE_TIMEOUT调大超时时间能缓解但要同时注意内存。另一类是多个用户同时编译CPU 核数不够队列累积所有项目都变慢。这时候最好限制 TeX Live 编译容器的 CPU 占用避免编译任务把主容器进程饿死。可以在 docker-compose 里给 sharelatex 服务设置deploy: resources: limits: memory: 6G编译队列卡死的另一个隐蔽原因是磁盘空间满了。Overleaf 的缓存和临时文件都在 /data 目录下如果项目历史记录很多磁盘经常悄悄膨胀编译也会无响应。用df -h查一下如果/opt/overleaf/sharelatex_data使用率超过 80%先清理历史版本或者扩容再重启服务。5.3 WebSocket 连接失败与页面无法实时刷新Overleaf 编辑器的实时协作依赖 WebSocket部署在反代后面时很容易出问题。如果页面能打开但保存和光标同步不正常多半是 WebSocket 没有被反向代理正确转发。我用 Caddy 做域名反代的时候需要在 Caddyfile 里明确开启 WebSocketoverleaf.example.com { reverse_proxy sharelatex:80 }Caddy 默认会处理 WebSocket但如果你用 nginx就得显式配置Upgrade和Connection头。这个问题的典型症状是浏览器控制台报WebSocket connection failed或者打开文档后白屏、一直加载中。经验如果你的部署环境是纯内网 IP 访问不涉及域名和反代一般不会触发这类问题。一旦上了域名和 HTTPS一定要把 WebSocket 转发配置好再上线否则看起来页面正常实际协作功能是残废的。5.4 用户注册管理和管理员权限社区版默认开放注册任何人都能在你服务器上建账号。公司内部用无所谓但如果是暴露到公网最好把公开注册关掉。做法是在环境变量里设置禁止注册相关的开关同时把初始管理员账号准备好。我初始化管理员用的是数据库直接改字段的办法先用一个账号注册进入 Mongo 容器把该用户的isAdmin改成true。具体操作docker exec -it overleaf_mongo mongosh use sharelatex db.users.updateOne({email: adminexample.com}, {$set: {isAdmin: true}})改完后刷新页面左侧菜单会出现管理后台入口。之后就可以在后台里管理用户和项目了。5.5 常见问题速查表现象可能原因排查方向sharelatex 容器反复重启MongoDB 未就绪或连接串错误docker logs看具体报错等 Mongo 就绪后手动重启登录后回到登录页SHARELATEX_SECURE_COOKIE与协议不匹配HTTP 环境设为 falseHTTPS 环境设为 true页面能打开但无法保存WebSocket 反代未配置检查反向代理的 Upgrade 头编译一直转圈然后失败超时太短或编译队列堆满调整COMPILE_TIMEOUT检查 CPU 和磁盘中文编译出来乱码引擎不是 XeLaTeX 或字体缺失使用 xelatex安装 Noto CJK 字体上传图片失败数据目录权限不对检查sharelatex_data写权限改属主某些宏包找不到TeX Live 镜像缺宏包扩展镜像并用tlmgr install安装6. 一点个人体会本地部署 Overleaf 这件事看起来只是拉几个容器真正麻烦的在于理解它的结构和运行逻辑。我第一周踩的最多的坑都来自环境变量和目录权限而不是 Overleaf 本身。建议刚上手的人不要急着追求跟官方同步升级先把 docker-compose.yml 的每个参数搞明白能说清为什么这么配再去做域名、HTTPS、备份这些进阶工作。部署完成之后我把备份脚本、升级步骤、常用宏包清单都整理成了团队内部文档。后续扩展还可以考虑接入 LDAP 统一认证、配置 S3 对象存储保存大文件、使用内网镜像仓库固定 TeX Live 镜像版本。按需逐步加就行不用一次全上。这套环境我们内部用了大半年稳定性一直不错也省去了跟在线版斗智斗勇的精力。
阅读完成 · 觉得有帮助?
咨询建站