从第一次在 Windows 上装了三个小时 TeX Live,结果编译论文时发现缺了一堆宏包、版本还和导师的模板不兼容,到后来切到 Linux 还是被各种依赖问题折磨,我一度觉得 LaTeX 这玩意儿就是劝退新人的。直到我试了 Docker 部署 TeX Live,第一次体验到一条命令拉起完整编译环境、换电脑零成本迁移的时候,才真正意识到原来 LaTeX 环境的正确打开方式是容器化。这篇文章我结合自己踩过的坑,把整个 Docker 部署 TeX Live 的完整方案、常用配置和问题排查写出来,适合正在被 LaTeX 环境折腾的论文党、想统一团队编译环境的技术负责人,以及准备把 LaTeX 编译做成自动化流水线的朋友。
1. 为什么非得用 Docker 来装 LaTeX 编译环境
1.1 传统本地安装到底有多痛
先说说我自己在本地装 TeX Live 的血泪史。第一次在 Windows 上安装,ISO 镜像解压出来好几个 GB,安装向导点来点去,大概花了将近两小时才装完。这还算顺利的,更头疼的是后面:
- 宏包版本冲突。MacTeX 和 Windows 版 TeX Live 的宏包更新时间不一样,同一份模板在两个平台上编译出来的排版效果居然有细微差别,页边距或间距偶尔差零点几毫米。
- 导师模板依赖各种旧宏包。有些学校或期刊提供的模板是几年前写的,默认依赖的宏包版本比较旧,新装的 TeX Live 2026 一编译就报错。为了迁就模板,你不得不去手动装旧版宏包,然后系统又提示和现有包冲突。
- 换电脑全重来。实验室的电脑、家里的电脑、笔记本,每台都要重新下载几百 MB 的安装包,装完还要再配置字体、环境变量、编辑器插件。
如果你只是写个简单文档,这些痛点可能不明显。但一旦写毕业论文、投期刊论文,模板复杂度上去之后,这些问题会被无限放大。我在写学位论文时,正文加图表加附录一共三十多个文件,因为本地宏包环境不一致,同一份代码在不同电脑上编译出来的目录页码居然不同,那种心情真的很难形容。
1.2 容器化方案的核心优势
Docker 把这些事全部打包掉,本质上就是一句话:你的编译环境就只是一个镜像,一个容器
- 花费时间从两小时变一分钟。拉完镜像直接编译,不用安装向导,不用配置路径。
- 环境完全一致。容器里是什么版本就是什么版本,不管你是 Windows、macOS 还是 Linux,编译效果完全一样。
- 不会污染宿主机。所有 TeX Live 相关文件都在容器里,删掉容器就干干净净,不用一堆卸载残留。
- 宏包和字体可以直接塞进镜像。包括那些很难找的中文字体、期刊专属字体,构建镜像时一次搞定。
- 换电脑迁移成本接近零。一个 Dockerfile,一条命令,新机器上直接复现全环境。
我个人觉得这项方案特别适合两类人:第一类是写毕业论文的学生,模板复杂、宏包依赖多,而且经常需要在学校机房、宿舍、图书馆之间换设备;第二类是实验室或项目组里有统一排版需求的技术团队,需要确保不同成员交上来的 LaTeX 文档编译环境一致。
2. 部署前你要知道的核心概念和镜像选型
2.1 Docker 分层镜像与缓存机制是怎么回事
很多人第一次用 Docker 部署 TeX Live 时,会被“镜像”“容器”“层”这些概念搞得有点懵。我尽量用大白话解释。
你可以把 Docker 镜像理解成一个压缩好的操作系统快照,它里面已经预装了所有需要的东西,比如 TeX Live 的二进制文件、字体、宏包。容器则是这个镜像的一个实例,就像你用同一个系统镜像装了多台虚拟机,每台都能独立运行。镜像里的每一层代表一个操作指令,比如“执行 apt 安装某个软件包”“从官网下载某个字体文件”,这些层是只读的,层叠在一起构成最终镜像。
构建镜像时有一个关键机制叫层缓存。如果你多次构建同一个镜像,只有发生改动的层会被重新构建,没改动的层直接复用缓存,这样能大幅节省时间和网络流量。比如你加了一个新字体文件,重新构建时只会重建添加字体之后的那几层,前面对系统库和 TeX Live 的安装层全部走缓存。
注意:如果你用卷挂载的方式工作,宿主机和容器之间的数据交换不会影响镜像层缓存。你在容器里改文件,宿主机同步就能看到,这是最常用也最灵活的工作模式。
2.2 三个主流镜像方案怎么选
用 Docker 部署 TeX Live,镜像方案大致有下面几种,我分别说下适用场景:
| 镜像方案 | 优点 | 缺点 | 适合人群 |
|---|---|---|---|
官方texlive/texlive | 版本全、平台多、持续更新 | 镜像较大(完整版约 4-6 GB) | 需要全量宏包、不太在意磁盘和拉取时间的人 |
| 第三方轻量镜像(如基于 Alpine) | 体积小、构建快 | 宏包不完全,缺失时需手动补 | 只写简单文档、对体积敏感的场景 |
| 自建镜像(Dockerfile 安装) | 完全定制、可控性最强 | 需要自己维护,构建时间长 | 有固定模板和字体需求、想要环境一致性的团队/个人 |
我自己的选择是官方镜像做底,再叠加自定义层。官方镜像里的 TeX Live 比较完整,常见宏包都有,一般学生模板都能直接用。但有些特殊字体或者小众宏包官方镜像里没有,这时候就自己在 Dockerfile 里加一层去装。这样既避免了从零构建的复杂,又能把特殊依赖固化下来。
2.3 版本选择和拉取加速的一些经验
镜像用latest标签还是指定版本标签?如果只是自己写文档,无所谓,用 latest 就行。但如果是团队协作或要配合论文模板长期使用,强烈建议固定一个具体版本,比如texlive/texlive:2025,这样可以保证大家用的宏包版本是同一批。
拉取镜像时,官方仓库在国内经常比较慢。这时候如果公司或学校有可用的内网镜像仓库,可以直接配置镜像加速地址,方法是在 Docker Desktop 的设置里把 registry-mirrors 指向加速地址。如果是 Linux 服务器,在/etc/docker/daemon.json里配置即可。我不具体说某个服务商的名字,你自己按自己网络实际情况选一个能用的、合规的加速源就好。
3. 从零开始:一条龙部署 TeX Live 到 Docker
3.1 宿主机安装 Docker 的三种方式
机器上还没有 Docker 的话,先把 Docker 装好。Windows 推荐装 Docker Desktop,这是最省事的方式,安装包下载完成后一路下一步就行,注意安装过程中会要求启用 WSL2 或者 Hyper-V,按提示重启即可。macOS 同样装 Docker Desktop,安装完打开应用,等右上角图标变成绿色就说明引擎已经启动。Linux 直接用系统命令装,Ubuntu/Debian 下执行:
sudo apt update sudo apt install docker.io sudo systemctl enable --now docker装完以后用docker --version验证是否成功,能看到版本号就说明 Docker 已经就绪。
提示:Windows 用户如果之前用过旧版 Docker Toolbox,先彻底卸载干净,避免和 Docker Desktop 冲突。这个坑我遇到过,安装报错查了半天,最后发现是两个版本抢同一个虚拟化端口。
3.2 拉取并验证 TeX Live 镜像
打开终端,执行下面的命令拉取官方镜像:
docker pull texlive/texlive:latest如果你是第一次使用,拉取时间可能比较长,完整版镜像有 4-6 GB 左右,具体看网络情况。拉取完成后用docker images看看镜像列表里是否出现texlive/texlive并且 TAG 为latest,看到就说明镜像已经就位。
接下来验证容器能正常运行。执行:
docker run --rm texlive/texlive:latest pdflatex --version这条命令的含义是:启动一个基于该镜像的临时容器,并执行pdflatex --version打印一下版本信息,执行完之后容器自动删除(--rm参数)。如果能看到类似 “pdfTeX 3.141592653” 这样的版本输出,说明环境已经能跑了。
3.3 创建你的第一个容器化 LaTeX 项目
镜像能跑pdflatex --version是基础,但真正要编译你的论文,肯定不能让容器里去敲命令、改文件,那样太蠢了。正确做法是用volume(卷)挂载的方式,把宿主机上的论文目录映射到容器内部的工作目录。
先在宿主机上建一个工作目录,比如~/latex-project,然后在里面新建测试文件test.tex:
\documentclass{article} \begin{document} Hello, Docker + \LaTeX{}! \end{document}然后运行:
docker run --rm -v ~/latex-project:/work -w /work texlive/texlive:latest pdflatex test.tex这里几个参数我拆开解释:
-v ~/latex-project:/work:把宿主机的~/latex-project目录挂载到容器内的/work目录,这样容器内读写的文件会和宿主机实时同步。-w /work:指定容器内的当前工作目录为/work。pdflatex test.tex:容器启动后自动执行的编译命令。
执行完以后去~/latex-project目录看看,应该能看到生成的test.pdf文件。到这里,一个最基础的 Docker 化 LaTeX 编译环境已经搭好了。
3.4 用 docker-compose.yml 把命令固化下来
每次都敲这么长一串docker run命令确实麻烦,而且容易记错参数。推荐用docker-compose.yml把配置固化下来,不仅记不住命令的问题解决了,团队里其他人也能直接一键启动同样的环境。
在项目目录下新建docker-compose.yml:
services: latex: image: texlive/texlive:latest container_name: my-latex volumes: - ./:/work working_dir: /work tty: true stdin_open: true配置好之后,在项目目录下只需要运行:
docker compose up -d docker compose exec latex bash第一条命令创建并启动容器,第二条命令进入容器内的交互式终端。之后你可以在里面执行xelatex test.tex、makeindex、bibtex等各种编译命令,就好像在本地装了一个完整的 TeX Live 环境。
在容器内操作完不需要手动exit退出吗?不是,需要exit退出容器终端,但容器本身通过docker compose stop停掉即可。数据都在挂载卷里,不会丢。
4. 编译中文论文的关键配置:字体、引擎和宏包
4.1 引擎选型:用 xeCJK 还是 ctex
LaTeX 编译中文文档,最核心的问题就是字体和中文排版。传统的pdflatex配合 CJK 宏包也可以处理中文,但用的时候比较折腾,字体编码问题尤其多。我现在几乎只用xelatex配合ctex宏包,这个组合对中文支持最友好:
ctex宏包自动处理中文排版细节,比如段落缩进、中文标点压缩、章节标题格式等。xelatex引擎直接支持系统字体,不需要像旧方案那样还要配置字体编码。
在 Docker 环境里,只要在documentclass中引用ctex宏包就能用了。示例:
\documentclass[UTF8]{ctexart} \begin{document} 你好,Docker 与 LaTeX! \end{document}然后编译命令也用xelatex:
docker run --rm -v ~/latex-project:/work -w /work texlive/texlive:latest xelatex -interaction=nonstopmode -synctex=1 test.tex4.2 中文字体缺失问题与解决方案
如果你直接拿上面这个命令编译中文文档,很可能会报一堆字体找不到的错误,因为官方 TeX Live 镜像里预装的中文字体很少。我自己遇到过的报错就是类似于 “The font ‘FandolSong-Regular’ cannot be found”。这种情况是因为ctex宏包默认依赖的中文字体没有齐全。
解决思路有两个方向:
方案一:在宿主机上装字体然后挂载进容器
把需要的中文字体文件(.ttf或.otf)放到一个目录,比如~/latex-project/fonts/,然后在挂载时多挂一个目录:
docker run --rm \ -v ~/latex-project:/work \ -v ~/latex-project/fonts:/usr/share/fonts/custom \ -w /work \ texlive/texlive:latest \ xelatex -interaction=nonstopmode test.tex前提是你把字体放到了容器内的字体目录,然后执行fc-cache -f刷新字体缓存。字体多的情况下每次启动容器都刷新一次,有点麻烦,但胜在宿主机上不用装 Docker 之外的东西。
方案二(我个人推荐):把这些字体固化到镜像里
如果你长期需要编译同一套论文模板,最省心的方式是在 Dockerfile 里把字体装好。下面是自定义镜像的示例Dockerfile:
FROM texlive/texlive:latest # 安装中文字体(以思源宋体、思源黑体为例) RUN apt-get update && \ apt-get install -y fonts-noto-cjk fonts-noto-cjk-extra && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* # 复制项目所需到镜像 COPY fonts/ /usr/share/fonts/custom/ RUN fc-cache -f构建自定义镜像:
docker build -t my-latex:2025 .之后用my-latex:2025替代默认镜像编译,字体问题就不会再出现了。整个镜像构建好以后,给团队其他人共享,大家拉下来就能获得一致的中文排版效果。
提示:如果学校单位有规定不能用
Noto CJK这类字体而必须用宋体、黑体等指定字体,你需要向相关版权方获取合法授权后,把相应字体文件拷贝到fonts/目录并重新构建镜像。版权问题务必自己把控好。
4.3 编译脚本:避免反复敲长命令
刚才我写了很多docker run带各种参数的命令,实际写论文的时候如果每次都敲一遍,还是会疯掉。建议在项目下写一个简单的编译脚本compile.sh:
#!/bin/bash IMAGE=my-latex:2025 WORKDIR=$(pwd) docker run --rm \ -v "$WORKDIR":/work \ -w /work \ "$IMAGE" \ latexmk -xelatex -interaction=nonstopmode -halt-on-error "$1"执行前先给脚本加执行权限:
chmod +x compile.sh之后每次编译只需要:
./compile.sh main.texlatexmk是非常好用的自动化编译工具,它会自动处理多次编译的依赖,比如交叉引用、目录、参考文献等,最终直接生成正确的 PDF。不加-halt-on-error的话,编译出错也会继续跑,生成的 PDF 可能不完整;加上以后,一旦报错马上停止,方便你快速定位错误位置。
5. 进阶玩法:把 LaTeX 编译能力迁移到编辑器、CI 与自动化场景
5.1 用 VS Code 的 LaTeX Workshop 配合容器编译
之前我一直建议在容器里执行编译命令,但对很多人来说,这样写论文太反人类了。大家更习惯在编辑器里写完一段就顺手编译预览。其实 VS Code 配合 LaTeX Workshop 插件,并且把编译方案指向容器内的命令,是能达到“编辑器里一键编译”的体验的。
具体做法是在 VS Code 的设置settings.json里配置 LaTeX Workshop 的编译 recipe,使用容器编译。大致思路是:
{ "latex-workshop.latex.recipes": [ { "name": "docker-xelatex", "tools": ["docker-xelatex"] } ], "latex-workshop.latex.tools": [ { "name": "docker-xelatex", "command": "docker", "args": [ "run", "--rm", "-v", "%DIR%:/work", "-w", "/work", "my-latex:2025", "xelatex", "-interaction=nonstopmode", "-synctex=1", "%DOC%" ] } ] }其中%DIR%是 LaTeX Workshop 提供的变量,代表当前打开的.tex文件所在的目录;%DOC%是当前文件名。这样你按Ctrl + Alt + B就能直接在容器里编译当前文档,预览效果和在本地装 TeX Live 完全一致。
实际使用中有一个小坑:LaTeX Workshop 默认根据自带的latexmk工具在本地找编译器,不会自动知道你要用 docker。你要在 recipe 中把工具名改成自己配置的docker-xelatex,并且latex-workshop.latex.recipe.default也改成对应 recipe 的名称,不然它还是会优先调用本地的 pdflatex 导致报错。
5.2 用 GitHub Actions 或 GitLab CI 做自动编译
Docker 化 LaTeX 在自动化方面更夸张的优势体现在 CI(持续集成)里。我现在习惯的做法是:论文全部存在 Git 仓库里,每次 push 到远程分支后,CI 自动用 Docker 镜像编译 PDF,然后把生成的 PDF 作为构建产物保存下来。这样团队审阅时根本不需要本地装 LaTeX,直接下载产物就能看。
以 GitHub Actions 为例,.github/workflows/compile.yml可以这样写:
name: Compile LaTeX on: [push] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Compile with Docker run: | docker run --rm -v $PWD:/work -w /work \ texlive/texlive:latest \ latexmk -xelatex -interaction=nonstopmode -halt-on-error main.tex - name: Upload PDF uses: actions/upload-artifact@v4 with: name: compiled-pdf path: main.pdf如果用的是 GitLab CI,.gitlab-ci.yml里同样能定义image: texlive/texlive:latest,然后直接执行编译命令。真正好的体验在于:不需要在 CI runner 上预先安装任何 TeX Live 相关的东西,runner 只需要有 Docker 环境即可。
5.3 定制化容器作为团队共享编译环境
如果你在实验室或公司里带项目,经常碰到新成员加入后环境不一致的问题,这种“共享编译环境”的模式更推荐。做法非常简单:
- 把
Dockerfile、字体目录、必要的宏包目录统一放在一个latex-env仓库里。 - 写好
docker-compose.yml和一份简短的 README,新成员只需 clone 仓库、安装 Docker、运行docker compose up -d。 - 写论文直接用挂载卷的方式,本机编辑、容器编译。
这样至少有三个好处:
- 新成员不用研究 TeX Live 怎么安装、宏包在哪下,拉起来直接用。
- 你们用的宏包版本、字体、编译参数完全一致,交叉审核文档时不会出现“我这边编译出来页码不对”的问题。
- 即使有人把容器搞坏了,删掉重建也就几秒钟的事,不会影响宿主机任何东西。
5.4 直接使用 TexLive 官方容器做文档协作
还有一种比较轻量的使用场景,我身边不少同学也在用:把自己平时写的小文档、公式笔记、作业等所有 LaTeX 源码放进一个目录,然后用官方镜像直接编译,不折腾任何自定义配置。因为你用到的宏包很有限,官方镜像完全覆盖。这种模式下,Docker 的角色就是“一个不占宿主机空间的 TeX Live 运行时”,随用随开,用完即删。
6. 实战中常见的坑与排查技巧
6.1 中文文档编译出来缺少字体或乱码的排查
这是出现频率最高的一个问题。报错信息通常是:
The font XXX cannot be found for character ...排查思路按顺序来:
- 先确认用的是
xelatex引擎编译,而不是默认的pdflatex。从报错信息很难看出来,但有中文内容时,pdflatex 经常会因为编码问题报错,而且乱码很严重。 - 再确认容器里确实有中文字体。执行这个命令,看字体列表里是否有中文字体的名字:
docker run --rm texlive/texlive:latest fc-list :lang=zh如果没有字体输出,就是缺少中文字体,按上面第 4 节给的 Dockerfile 方案补装字体。
- 如果字体存在仍报错,检查
ctex宏包是否可以正常加载。可以在文档开头用\usepackage{ctex}然后编译一个最小示例,逐步排查。
6.2 编译超时或命令行卡死怎么办
如果编译文档较大,图片多、交叉引用多,容器编译时间会比较长。如果你用 VS Code 的 LaTeX Workshop 调用 docker 编译,可能遇到默认超时时间不够长的报错,大文档还没编译完就提示超时。解决办法是在 VS Code 设置里增加超时时间:
{ "latex-workshop.latex.build.timeout": 300 }单位是秒,按你的文档复杂度调整。我写学位论文时大概要编译 1-2 分钟,设为 300 秒绰绰有余。
如果是命令行里直接编译卡住,大概率是有等待输入操作,最常见的是xelatex编译时遇到了错误询问,默认会停下来等待用户输入。解决办法是在命令中加-interaction=nonstopmode,让它遇到错误直接就停下来,而不是等待输入。
6.3 镜像磁盘占用过大怎么清理
官方镜像动辄几个 GB,如果长期拉取多个版本,磁盘占用会非常大。可以用docker system df查看各个镜像和容器的占用,然后用docker system prune -a清理不再使用的镜像。如果当前项目需要保留某个特定版本,别加-a,只执行docker system prune删掉悬空数据即可。
删除镜像和容器:
docker rm $(docker ps -aq) docker rmi $(docker images -q)这两个命令会把所有容器和镜像都清理掉,务必确认不是在生产环境执行。新手建议一条一条看确认后手动删除。
6.4 常见错误排查速查表
| 症状 | 可能原因 | 解决方向 |
|---|---|---|
| 编译出来没有 PDF 文件 | 文档有语法错误导致编译终止 | 加-halt-on-error查看首个报错位置 |
| 中文乱码 | 使用了pdflatex而非xelatex;编辑器存储编码不是 UTF-8 | 改用 xelatex;以 UTF-8 编码保存文件 |
| 交叉引用显示 ?? | 只编译了一次;latexmk能自动多次编译,直接docker run只执行一次只会有警告 | 使用 latexmk 或连续执行 xelatex 两次 |
| 参考文献编译报错 | 缺少.bib文件;bibtex 命令执行顺序不对 | 用 latexmk automake 自动处理 biber/bibtex |
docker: command not found | Docker 未安装或未启动 | 先安装 Docker 后重新执行命令 |
用docker run --rm时每次都会产生一个新的容器,虽然不占空间,但--rm参数没加的话,编译失败后容器会残留,积少成多也很占磁盘,也是容易忽略的坑。可以定期执行docker container prune清理。
7. 我看完整个过程后的几个实际体会
将我长期用 Docker 部署 TeX Live 的经验浓缩一下。官方镜像直接用,基本能覆盖 80% 的日常需求;真正需要自定义的往往是中文字体和几个特殊的宏包,这部分通过 Dockerfile 叠加一层即可。VS Code 编辑器联动容器编译,体验上已经接近本地安装完整版 TeX Live 的感受。CI 自动编译的模式尤其在多人协作或投期刊稿件时可以明显减少“我这里能编译为什么你那里报错”的沟通成本。
最终给个建议:别追求一开始就搞特别复杂的大而全定制镜像,先用官方镜像把论文编译跑通,再逐步叠加字体、宏包,直到满足你的模板要求为止。等镜像稳定后,这套配置能让你未来几年都省心不少。