Docker 部署 TeX Live 这事,我琢磨了好一阵子才下定决心搞。之前一直在自己电脑上折腾 LaTeX 环境,每次换电脑或者帮师弟师妹配环境,都像重新历劫一次。直到我把整套编译环境塞进 Docker 镜像之后,才发现以前那些安装 LaTeX 发行版、配置环境变量、解决字体报错的破事,全都可以一次性理清。这篇内容就是把我的完整部署过程、踩过的坑、以及最终沉淀下来的一套可直接照搬的工作流写出来,适合那些被 LaTeX 环境折腾得够呛、又不想在论文写作前夜倒腾依赖关系的朋友。
1. 为什么要在 Docker 里编译 LaTeX:先算一笔环境账
从本科写毕设开始,我就在用 LaTeX 排版论文,几乎每一台新电脑上都要经历一遍完整的 TeX 环境安装流程。Windows 上装 TeX Live 要跑几十分钟的 installer,macOS 上装 MacTeX 那个安装包动辄几个 G,好不容易装完,第一个工程编译又可能蹦出一堆宏包缺失的报错。等到真正需要协同工作时,大家用的版本、宏包、字体各不相同,A 同学编译通过的文件在 B 同学电脑上就是无数个红色的叉。
1.1 本地安装 TeX Live 的三个绕不过的痛点
先说痛点,这直接决定了为什么 Docker 方案值得尝试。第一个痛点是安装体积与耗时的双重考验。完整安装 TeX Live 的话,硬盘上要腾出 7 到 10 个 G 的空间,安装过程因为要解压数千个小文件,耗时往往超过半个小时。哪怕是只装 scheme-small 这种精简版本,后面写论文时候仍躲不掉中途补装宏包的操作,每次都要跑到 TLManager 里慢慢勾选。
第二个痛点是宏包环境的一致性难以保证。LaTeX 生态里宏包版本一更新,可能出现兼容性变化,今天还能编译的项目,明年可能因为某个宏包升级就挂了。如果在老项目上固定宏包版本,又会影响其他依赖新版宏包的新项目。这种依赖管理问题,在原生安装方案下非常棘手。
第三个痛点是清理与卸载的麻烦。装有完整版 TeX Live 的系统,卸载时要删掉的目录、注册表项、local 树散落各处。我遇到过卸载之后再装其他发行版,结果路径冲突导致工作区里的旧 .fmt 文件还在被调用。新手遇到这种情形,往往没有头绪。
1.2 容器化方案的思路:环境即代码
把 TeX Live 装进 Docker,本质上是把过去那套"在一台机器上装环境"的逻辑,替换成"用镜像定义环境"的逻辑。镜像就是一个打包好的 Linux 文件系统,里面有完整的 TeX Live 安装目录、全局字体配置、以及预设好的编译脚本。你想用的时候,基于这个镜像跑一个容器,把你本机的工作目录挂载进去,在容器里执行编译命令,产物直接就落在宿主机当前目录里。
这个方案的收益,往小处说是省掉了重复安装时间,往大处看是类团队协作时环境完全可复制。我拿着一个 Dockerfile,就能在办公室的 Linux 工作站、家里的 Windows 笔记本、甚至是 CI 服务器上构建出一模一样的编译环境。版本一致性、宏包固定、字体配置全都在镜像层面锁死。编译一遍没问题,换台机器重新构建镜像,结果一样。
当然,容器化也不是没有代价。启动容器完成一次编译,毕竟比直接敲pdflatex多了一点运行时开销。另外,如果你对 LaTeX 宏包、字体机制完全没有了解,遇到镜像里缺个别宏包时还是得有基本的查错能力。不过,对于绝大多数写论文、写报告的场景,这点成本完全值得。
2. 动手之前的准备:选对镜像和工具
2.1 Docker 环境安装:Windows、macOS、Linux 三个平台
既然要跑容器,宿主机必须先有 Docker 运行时。Windows 和 macOS 上一般装 Docker Desktop,Linux 上则是 docker-ce 或发行版自带的 docker 包。
Windows 用户要格外注意虚拟化支持的开关。装上 Docker Desktop 之后如果启动失败,报错信息里常有 Virtualization support not detected 或者 WSL 2 kernel 相关提示。这时候去 BIOS 里确认 Intel VT-x 或 AMD-V 已经开启,同时确认 Windows 功能里的"适用于 Linux 的 Windows 子系统"和"虚拟机平台"两项已经勾上。这套流程我在多台机器上试过,属于最常规的解法。
macOS 用户相对省心,Intel 芯片和 Apple Silicon 芯片都支持 Docker Desktop,但镜像跑起来会有架构差异。好在 TeX Live 镜像我们基本只做计算,不做图形界面或硬件直通,x86_64 镜像在 Apple Silicon 上通过 Rosetta 模拟或 arm64 重制版镜像都能正常编译。
Linux 服务器或工作站用户直接在终端执行sudo apt install docker.io或sudo dnf install docker-ce,装完把当前用户加入 docker 组,避免每次敲命令都要 sudo。装完跑一个docker run hello-world验证,能正常回显说明运行时已经就绪。
2.2 TeX Live 镜像怎么选:从官方源到第三方定制版
Docker Hub 上有多种 TeX Live 镜像,我实际用下来主要分三类。第一类是官方镜像占位,texlive/texlive这个名字多年没有活跃维护,基本可以忽略。第二类是社区常用的papeeria/texlive,这个镜像基于 Ubuntu,内置了较完整的 TeX Live 集合,历史组织里很多自动化编译流程都在用它。第三类是纯 Alpine 基座的精简镜像,比如tianon/texlive,体积小很多,但宏包完整度就要看运气了。
从我写论文和做技术文档排版的实践经验看,最稳妥的还是自己基于 Debian 或 Ubuntu 的官方镜像,在 Dockerfile 里拉取 TeX Live 的官方 ISO 或通过网络安装方式构建。这样镜像里包含哪一套宏包、补丁版本打到多少,全部由自己把控。只图便利的,可以直接拉一个papeeria/texlive,本文后面的操作步骤也基于这类镜像来说明。
说到版本选择,2026 这种最新的预发布版本并不建议贸然使用,尤其是你的论文模板对某些宏包存在函数签名级依赖时,更新版本带来的隐性破坏比升级红利更常见。我在线上环境固定用的是 TeX Live 2022 到 2024 之间的稳定版本,既保证宏包数量,又不至于引入新版兼容问题。
2.3 镜像体积与运行时的高效平衡策略
完整 TeX Live 镜像解压后通常有 5 到 8 个 G,拉取过程需要点耐心。如果你只做日常文章、报告和 PPT 排版,可以考虑精简安装。通过tlmgr按需安装宏包,基础镜像只装 scheme-infraonly(基础设施),之后把论文需要的宏包逐个加入 Dockerfile。
FROM debian:bookworm-slim RUN apt-get update && apt-get install -y \ perl \ wget \ fontconfig \ && rm -rf /var/lib/apt/lists/* RUN wget http://mirror.ctan.org/systems/texlive/tlnet/install-tl-unx.tar.gz \ && tar -xzf install-tl-unx.tar.gz \ && cd install-tl-* \ && ./install-tl --profile=/dev/stdin <<EOF selected_scheme scheme-infraonly TEXDIR /usr/local/texlive TEXMFCONFIG ~/.texlive/texmf-config TEXMFHOME ~/texmf TEXMFLOCAL /usr/local/texlive/texmf-local TEXMFSYSCONFIG /usr/local/texlive/texmf-config TEXMFSYSVAR /usr/local/texlive/texmf-var TEXMFVAR ~/.texlive/texmf-var option_doc 0 option_src 0 EOF这个方案剪完体积能控制在 2 个 G 左右,编译常规文章没什么压力。如果哪次编译报缺宏包,就在 Dockerfile 里补一行RUN tlmgr install <包名>然后重新构建镜像。所有变更都在镜像版本控制里,方便回溯。
3. 完整部署实操:从起容器到出 PDF
3.1 最新镜像拉取与容器起步
真正动手的第一步,是把基础镜像拉下来。我们在终端里执行:
docker pull papeeria/texlive:latest如果网速一般,耐心等一会儿,拉完用docker images确认镜像 ID。接下来准备一个工作目录,我习惯在 Linux 或 Windows WSL 2 下用/work/paper这类路径,目录下面放main.tex和图表文件。
启动容器并进入交互式 shell 的方式:
docker run --rm -v /work/paper:/work -w /work -it papeeria/texlive:latest /bin/bash这里解释一下参数含义:--rm表示退出后直接删除容器,-v把宿主机目录挂载到容器的/work,-w设定工作目录为/work,-it表示分配一个交互式终端。这么做的效果是,你在容器内对/work里文件做的任何操作,都直接映射回本机,容器删除后文件依然完好。
3.2 第一次编译:用 Hello World 验证环境
进入容器后,先确认编译工具链存在:
which pdflatex which xelatex which latexmk然后写一个最小文档体验全流程:
\documentclass{article} \usepackage{ctex} \begin{document} 你好,LaTeX。 \end{document}用xelatex编译,因为ctex宏包配合中文文档时,XeLaTeX 引擎表现最稳定:
xelatex main.tex如果终端里正常滚过几十行日志,最后生成main.pdf,那么整套环境已经跑通。在宿主机工作目录里看到 PDF 文件的一瞬间,那种感觉和本地装完 TeX Live 后第一次编译成功是一样的,但整个过程可重复、可分发。
这里还要提一个细节:如果你在编译中文字档时遇到ctex报错或者字体缺失,大概率是容器的 fontconfig 缓存里没有中文字体。下一节会展开说明,这是因为 Debian 系镜像默认的中文字体集不太全,需要显式安装。
3.3 封装常用编译命令:latexmk 配置文件
手动一条条敲编译命令太原始。LaTeX 生态里,latexmk是最省心的自动化编排工具,它会根据文件依赖关系自动决定编译次数,并在需要时调用 bibtex、makeindex 等辅助程序。
在宿主机工作目录创建.latexmkrc:
$pdf_mode = 4; $pdflatex = 'xelatex -synctex=1 -interaction=nonstopmode -file-line-error %O %S'; $bibtex = 'bibtex %O %S'; $makeindex = 'makeindex %O %S'; $DVIpdf = 'xelatex -synctex=1 -interaction=nonstopmode -file-line-error %O %S';然后进容器执行:
docker exec -it <容器名> latexmk -xelatex main.tex如果不想进入容器 shell,也可以直接用docker run一次性执行:
docker run --rm -v /work/paper:/work -w /work papeeria/texlive:latest latexmk -xelatex main.tex这种写法的好处是容器启动、编译、退出全自动,宿主机上除了工作目录外不残留任何状态。我把这个命令做成了 shell 函数放进~/.bashrc:
function texbuild() { docker run --rm -v "$(pwd)":/work -w /work papeeria/texlive:latest latexmk -xelatex "$1" }之后在任意论文目录里敲一行texbuild main.tex,PDF 就出来了。配合 VS Code 的 LaTeX Workshop 插件,还能把 Docker 设定为默认编译容器,这个后面会讲。
4. 论文场景的核心壁垒:中文支持与模板适配
写英文论文用默认的 article 文档类就没什么波澜,可到了中文毕业论文、课程大作业、期刊投稿阶段,问题就逐渐浮出来。目前国内容易出现的情况是:模板给的示例文档用 CTeX 或 xeCJK 排版,对字体和编译引擎有严格讲究,如果容器里字体缺失或引擎不对,排版效果直接崩掉。
4.1 中文字体的容器化配置:不是装个包那么简单
很多 Debian 系镜像默认没有中文字体,ctex宏包在主文档加载时会调用系统字体。容器内如果没有中文字体,XeLaTeX 会报font-not-found错误,或者退回到一个根本不对的字体,导致中文显示成方块。
我第一次在容器里编译中文文章时就遇到这个问题,当时排除了一个多小时,最后发现只是字体没装。解决办法是在 Dockerfile 里安装fonts-noto-cjk,这是一套完整的 Noto CJK 字体,包含宋体、黑体、楷体的对应字型,CTeX 宏包在 fontset 参数设为fandol或noto时都能直接匹配。
RUN apt-get update && apt-get install -y fonts-noto-cjk \ && fc-cache -ffc-cache -f是刷新 fontconfig 缓存,没有这一步系统可能还是找不到刚装的字体。构建新镜像后,运行:
docker exec -it <容器名> fc-list :lang=zh能看到 Noto Sans CJK SC 和 Noto Serif CJK SC 的输出就算配置成功。
如果你用的是 Windows 本地字体,可以把宿主机的simsun.ttc、simhei.ttf挂载进容器,不过我对这种做法不太推荐——它把容器的可移植性又拖回了依赖宿主机文件的泥潭。除非学校模板明确要求必须有宋体或黑体,否则用 Noto CJK 就够了。
4.2 从模板到容器:让论文模板顺利跑起来的一揽子方案
我曾帮一个学弟迁移他的硕士论文模板到 Docker 环境,那个模板基于学校的 cls 文件,内部调用了ctexbook、gbt7714参考文献著录宏包、ntheorem定理环境等,初次在容器里编译直接挂了。排查后发现缺了三个宏包:texlive-lang-chinese、texlive-science、texlive-bibtex-extra。
在精简型镜像上,解决宏包缺失的标准操作是tlmgr install <包名>。但如果是papeeria/texlive这类基于 Ubuntu + apt 管理宏包的镜像,也可以直接apt-get install texlive-lang-chinese。注意不同镜像的宏包管理方式不一样,建议在容器里先执行tlmgr --version看走的是哪套体系。
tlmgr install titlecaps tlmgr install ntheorem tlmgr install gbt7714安装完跑一次kpsewhich ntheorem.sty,如果返回路径说明宏包已可用。
针对模板里常见的\input{...}相对路径和图片文件夹,容器内的工作目录已经映射到宿主机整个论文目录,理论上不存在路径问题。但有一个细节必须提醒:Windows 用户在宿主机用反斜杠\路径,而在容器内是 Linux 文件系统,路径分隔符永远是/。如果论文模板里用了硬编码的 Windows 路径,迁移到容器后会直接报No such file or directory,需要打开 cls 或 tex 文件里的路径定义修正。
4.3 参考文献与交叉引用:BibTeX 的容器内玩法
论文场景避不开参考文献管理。很多新手在本地装好环境后写完正文,第一次跑 bibtex 也会被繁琐的流程绕晕。Docker 环境下,只要你用的是latexmk编排,bibtex 这一步会被自动触发,不需要手工介入。
不过这里有个踩坑点:如果你的主文档使用\bibliographystyle{gbt7714}这类国内期刊样式,需要确认样式文件在容器内已安装。缺少.bst文件时,bibtex 会报I couldn't open style file gbt7714.bst。解决办法同样是先kpsewhich gbt7714.bst查一下,没有就tlmgr install gbt7714。
我见过不少人在容器里遇到引用编号全是问号的情况,这不是环境问题,而是编译顺序不对:先xelatex编译一次生成.aux,再bibtex处理参考文献数据库生成.bbl,然后再xelatex两次解析交叉引用。用latexmk一条命令自动完成这些,确保容器环境里优先使用它。
5. 效率提升与疑难杂症排查
5.1 编译效率优化:镜像预热与增量缓存
容器跑编译有一个先天弱点:每次启动都是全新文件系统,Workspace 里如果有大量编译中间文件,硬生生地重新生成会比较耗时。好在latexmk默认会检测源文件时间戳变化,如果.aux、.bbl这些中间文件在挂载目录里存在,它不会重复跑无用的编译轮次。
想要更激进一点,可以给容器设置一个常驻方式,这样字体缓存、宏包格式文件.fmt都能留在容器层,不用每次重新生成。启动一个带名字的常驻容器:
docker run -d --name texlive-daemon -v /work/paper:/work -w /work papeeria/texlive:latest sleep infinity之后要编译的时候直接:
docker exec texlive-daemon latexmk -xelatex main.tex这种方式省掉了每次启动容器的开销,实测对于大型论文,编译时间能减少 20% 到 30%,因为 XeLaTeX 需要的格式文件不需要每次重建。不过要注意,这个容器会一直占用系统资源,所以在论文忙完后记得docker stop texlive-daemon。
还有一个小技巧是设置环境变量让 xelatex 缓存字体信息。在.latexmkrc里加一行:
$xelatex = 'xelatex -synctex=1 -interaction=nonstopmode -file-line-error -halt-on-error %O %S';-halt-on-error让编译在第一个错误处停止,避免刷屏式报错。对论文排版这种长文档场景,这个开关能帮你快速定位语法问题,不用滚动几百行日志找第一个 error。
5.2 高频报错速查表:容器内最常翻车的五种姿势
| 报错信息 | 根因 | 解决动作 |
|---|---|---|
| font-not-found for "NotoSerifCJKsc" | 中文字体未安装 | 安装 fonts-noto-cjk 并执行 fc-cache -f |
! LaTeX Error: File 'xxx.sty' not found | 缺少对应宏包 | 执行tlmgr install xxx或 apt 安装对应 texlive 组件 |
! I can't write on file 'main.pdf' | 目录权限不足或挂载目录未正确映射 | 检查 docker run 的 -v 参数,确保工作目录是 /work |
! Emergency stop | 语法错误或依赖文件缺失导致 fatal error | 查看错误信息上方第一个!前的内容,通常是缺少文件或宏包冲突 |
/bin/bash: latexmk: command not found | 基础镜像里没有安装 latexmk | 在 Dockerfile 里加RUN tlmgr install latexmk或 apt 安装 |
这些错误里面,宏包缺失发生的频率最高。我的排查习惯是,先把报错里提到的.sty文件放到搜索引擎或 CTAN 上查一下属于哪个宏包集,再决定安装策略。比如geometry.sty属于 texlive-latex-base,booktabs.sty属于 texlive-latex-extra,algorithm.sty属于 texlive-science。如果用的是默认完整镜像,这类问题基本不会碰到。
5.3 与编辑器集成:VS Code LaTeX Workshop 配置实战
搭好了 Docker 编译环境,日常写作如果还靠命令行切来切去,体验上还是不够顺畅。把 VS Code 的 LaTeX Workshop 插件和 Docker 容器串起来,才能在写论文时享受双屏编辑、正向定位、编译错误高亮一条龙。
在 VS Code 的首选项 JSON 里加入:
"latex-workshop.latex.recipes": [ { "name": "Docker latexmk", "tools": ["Docker latexmk"] } ], "latex-workshop.latex.tools": [ { "name": "Docker latexmk", "command": "docker", "args": [ "run", "--rm", "-v", "%DIR%:/work", "-w", "/work", "papeeria/texlive:latest", "latexmk", "-xelatex", "-synctex=1", "-interaction=nonstopmode", "%DOC%" ], "env": {} } ]这里%DIR%是当前文件所在目录,%DOC%是主文件路径。保存配置后,打开.tex文件,点击插件侧边栏的"Recipe: Docker latexmk"按钮,VS Code 会调用 Docker 容器完成编译,并直接把 PDF 嵌入到预览窗口里。SyncTeX的正向和反向搜索也一起生效,从 PDF 双击可以跳到源码对应行,写长论文时找内容方便很多。
这里提一个配置细节:Windows 上如果 Docker Desktop 做路径映射,-v %DIR%:/work里的C:\Users\...路径格式 Docker 能自动识别,但如果遇到中文路径或空格的目录名,需要在%DIR%前后加双引号处理,不然会被拆成两个参数。稳妥做法是把论文目录全部放在不带空格的纯英文路径下,省掉很多不必要的烦恼。
5.4 镜像瘦身与团队分发:把环境打包给所有人
论文写完之后还有一个常见需求:把编译环境分发给合作者或导师。Docker 镜像天然适合做这件事。如果你用的是自己构建的 Dockerfile,在宿主机执行docker build -t my-texlive:2024 .生成镜像,推送到私有 registry 或者导出为 tar 包:
docker save my-texlive:2024 | gzip > my-texlive.tar.gz对方拿到tar.gz后执行docker load < my-texlive.tar.gz,然后按照前面同样的命令启动容器,就能获得一模一样的编译环境。这比让合作者自己装 TeX Live 再手动同步宏包版本要可靠太多。我甚至见过一个实验室把统一论文容器做成内部标准环境,所有新生入学后先拉镜像再提交开题报告,导师再也不用面对"我这编译不过"的求助消息。
如果你对镜像体积有强迫症,还可以在 Dockerfile 末尾清理 apt 缓存和 TeX Live 安装包临时文件,把镜像体积压缩 1 到 2 个 G。不过说实话,少了这部分体积换个可靠稳定的完整环境,性价比还是很高的,就看你的网络带宽和磁盘空间了。
6. 一些额外的个人实战心得
做这套 Docker 编译环境以来,最明显的感受就是环境的确定性大幅提升。过去在笔记本上写论文,隔三差五出现"昨天还能编译,今天怎么不行",大部分原因是系统升级或宏包自动更新带来的副作用。现在镜像锁定了全部环境因素,只要不主动重新构建,每次编译的输入输出逻辑完全一致。
针对还没入坑的朋友,我建议不要一上来就追求精简镜像或自定义 Dockerfile,先用papeeria/texlive拉一个开箱即用的环境跑通流程,之后再逐步压缩体积、固定版本、加入自己的模板和字体。这个循序渐进的过程,比一开始就强行啃 TeX Live 的安装机制友好得多。
另外给一个偏门但很有价值的操作:把 Dockerfile 和 .latexmkrc 放进论文项目仓库里。这样代码托管平台上每次提交代码,CI 流程都能在 Docker 容器里跑一次编译验证,确保任何时刻拉下来的项目都能直接产出 PDF。这对团队合作、课程助教批量收作业、以及自己长期维护的长期研究项目都特别实用。
还有一个小细节,如果你用 Overleaf 配合本地 Docker 双保险,最好注意一下宏包版本差异导致的结果不一致。这两年我遇到过 CV 模板在 Overleaf 上是 v2.1,本地镜像里还是 v1.9,渲染出的配色和间距有细微差别。遇到这种不一致,以固定版本的容器结果为准,毕竟本地镜像的版本是锁死的,不会像在线平台一样偷偷更新。
最后再分享一个省时间经验:每次编译完,习惯性地在工作目录里把.aux、.log、.out这些中间文件删除或交给.gitignore忽略,避免容器挂载目录中积累大量无用文件。前几次编译必须要的缓存文件在.latexmkrc和 Work 目录结构不变的情况下都能重新生成,真正长期保留的只有主文档、图片、样式文件和最终的 PDF。一年下来,我的每个论文工作区都干净清爽,和新开一个项目没有两样。