Docker容器化LaTeX编译环境:论文排版自动化完整指南
2026/9/14 18:37:39 网站建设 项目流程

又到了写论文的季节。每年这个时候,总有人被 LaTeX 环境和宏包折腾到怀疑人生——CTeX 乱码、缺少 sty 文件、版本不一致、在 Windows 上装完 TeX Live 之后发现还要配字体,一不小心把系统目录搞得一团糟。我自己的做法是:用 Docker 把 TeX Live 环境整个封装起来,不管笔记本里装了什么系统、装没装过 LaTeX,一条命令就能把论文编译出 PDF。这套方案我已经用了好几个项目,从期刊模板到学位论文都在跑,稳定省心。这篇文章就把完整的部署思路、实操步骤和踩坑记录整理出来,给准备写论文或者做排版自动化的人一个可以直接照抄的方案。

1. 为什么要把 LaTeX 编译环境塞进 Docker

1.1 那些年我们一起踩过的 LaTeX 环境坑

LaTeX 本身只是个宏包系统,但实际用起来,你面对的是 TeX Live、CTeX、MiKTeX、宏包、字体、编译引擎这一整套东西。最常见的坑有三个。

第一个是版本地狱。TeX Live 每个版本对应一套宏包快照,2022 年写的模板,拿 2025 年的发行版编译可能因为宏包更新而报错;反过来,新模板在老发行版上又可能缺宏包。很多时候不是你写错了,而是环境版本和模板要求不匹配。

第二个是依赖不干净。装了完整的 TeX Live full 版本确实省心,但体积动辄 7、8 个 GB,而且它往系统目录里塞了大量文件。等到你想升级或者卸载的时候,一堆残留文件怎么清都清不干净。我之前在 macOS 上手动卸载 TeX Live 就折腾了大半天。

第三个是字体和中文字体问题。论文写作绕不开中文,而中文排版要用 xelatex 配合 ctex 宏包,还需要中文字体。Windows 上字体还好说,到了 Linux 服务器上,默认连一个中文字体都没有,编译出来的 PDF 全是方块。

这些问题单独处理都不算难,但组合在一起,每换一台机器就要重新折腾一遍,时间和精力全都耗在环境上了。

1.2 容器化到底解决了什么问题

Docker 的思路很简单:把 TeX Live 环境连同依赖一起打包进一个镜像里,编译的时候让容器去跑,编译完 PDF 落盘,容器随即销毁。宿主机不需要安装任何 TeX 相关的东西。

这套方案解决了三个关键问题:

  • 环境可复制。同一个镜像,在 Windows、macOS、Linux 上的行为完全一致。不同项目可以用不同版本的 TeX Live 镜像,互不干扰。
  • 宿主机干净。容器是隔离的,往里面装多少东西都不会污染系统。不用了直接删镜像,所有痕迹一起消失。
  • 自动化友好。CI/CD 平台里跑 Docker 是天然的能力,提交代码后自动编译 PDF 非常自然,这也是后面要讲的进阶玩法的基础。

对我来说,最直接的感受是:以前给师弟师妹们搭论文环境要远程指导一晚上,现在只需要给他们一份 Dockerfile 和一个启动脚本,跑起来就能编译,世界清净了很多。

1.3 什么人适合这套方案

虽然 Docker 有一点学习成本,但下面这几类人我强烈建议用:

  • 学生。论文模板经常要换学校、换期刊,每个模板对宏包版本的要求不一,容器隔离能让你放心折腾。
  • 跨平台用户。白天实验室用 Linux,晚上笔记本用 Windows,用 Docker 之后不需要在两台机器上分别维护环境。
  • 做排版自动化的人。如果你的论文是多人协作、需要自动化出 PDF,那 Docker 是标准答案。
  • 被 LaTeX 环境折腾怕的人。如果你曾经在一个 LaTeX 环境上耗掉超过两个小时,这方案大概率适合你。

当然,如果你只是偶尔写一页简历,那没必要上 Docker,直接装个精简版 TeX Live 就好。这套方案适合把 LaTeX 当作严肃生产力工具的场合。

2. 方案选型:现成镜像还是自己装

2.1 直接使用现成的 TeX Live 镜像

Docker Hub 上的官方镜像更新不太稳定,我目前主力用的是 ghcr.io/texlive/texlive 这个镜像,它由 TeX Live 社区维护,版本跟随 TeX Live 年度发布,地址是ghcr.io/texlive/texlive,tag 有20242025latest等。

这个镜像的好处是它直接基于 TeX Live 官方安装脚本构建,包含完整的系统目录结构,用latexmktlmgr都比较顺手。缺点是完整版镜像很大,拉取的时候要有耐心,我在网络好的环境下拉一次也要几分钟。

如果是纯英文文档,或者对中文没有硬需求,也可以用更小的镜像,比如结合 Pandoc 生态的pandoc/latex,或者自己按需裁剪。但对于写中文论文的用户,我建议直接上完整版镜像,省得后面缺这个缺那个。

提示:镜像大不是问题,拉取占用的是一次性流量和磁盘空间。真正天天打交道的是编译速度,而这个取决于 CPU 和镜像是否在本地缓存,和镜像体积关系不大。

2.2 自己写 Dockerfile 定制环境

现成镜像不满足需求时,可以自己写 Dockerfile。比如你只需要 xelatex + ctex + 常见宏包,那用 Ubuntu 基础镜像 + 精简 TeX Live 包就能得到一个比完整版小得多的环境。

下面是我自己用过的精简 Dockerfile:

FROM ubuntu:22.04 ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y --no-install-recommends \ texlive-latex-base \ texlive-latex-recommended \ texlive-latex-extra \ texlive-fonts-recommended \ texlive-xetex \ texlive-lang-chinese \ fonts-noto-cjk \ latexmk \ lmodern \ && rm -rf /var/lib/apt/lists/* WORKDIR /workdir

构建命令:

docker build -t my-texlive:2025 .

这里的关键包说明:

  • texlive-latex-extra:覆盖面很广的宏包集合,论文模板里常见的algorithmlistingsenumitem等都在里面。
  • texlive-xetex:xelatex 引擎,中文论文必须。
  • texlive-lang-chinese:ctex 宏包和中文支持。
  • fonts-noto-cjk:思源黑体的系统字体版本,解决容器内找不到中文字体的问题。
  • lmodern:Latin Modern 字体,很多模板依赖它。

我自己在实验室服务器上就是用这个方案,镜像体积比完整版小了一半多,编译常见论文模板基本不缺东西。

2.3 我的推荐与取舍

直接给结论:新手和追求省心的人,用ghcr.io/texlive/texlive:latest完整版;想控制镜像体积、或者公司/学校内网拉取镜像不方便的人,自己构建精简版。

还有一个思路是“完整版镜像 + 按需安装缺失宏包”。容器的好处是你可以进入容器里用tlmgr install 包名补装,然后用docker commit把当前容器保存成新镜像。这个方案适合那种只有一两个宏包缺失的场景,比维护一堆 Dockerfile 省事。

不过有一点要注意:docker commit会产生较多历史层,镜像管理起来不够优雅。我更推荐的方式是,把需要的宏包写入 Dockerfile,用tlmgr安装后重新构建,这样镜像的可复现性最好。

3. 动手部署:从拉取镜像到第一次编译成功

3.1 先确认 Docker 环境正常

不管你是 Windows、macOS 还是 Linux,先确认 Docker 本体能用:

docker version

Windows 用户要特别注意:Docker Desktop 依赖 WSL2 或者 Hyper-V,首次安装后大概率会遇到“Docker Desktop failed to start because virtualisation support wasn't detected”之类的报错。这是因为 BIOS 里虚拟化没开,或者 Windows 功能里“虚拟机平台”没开启。解决办法稍后在第 6 章详细说,这里先保证docker run hello-world能跑通再说下一步。

3.2 拉取 TeX Live 镜像

确定能跑 Docker 之后,先拉镜像:

docker pull ghcr.io/texlive/texlive:latest

网络正常的情况下,这个命令会把整套 TeX Live 环境拉下来。如果你的网络非常慢,可以配置 Docker 的 registry-mirrors 选项,这是 Docker 官方支持的功能,配置为你的云服务商或学校提供的镜像地址,拉取速度会有明显提升。

拉完验证一下:

docker run --rm ghcr.io/texlive/texlive:latest --version

能看到 TeX Live 和版本信息,说明环境已经可用了。

3.3 编译第一份 PDF

现在创建第一个测试工程。新建一个目录,写入一个最小 LaTeX 文件:

\documentclass{article} \begin{document} Hello, Docker LaTeX! \end{document}

然后执行编译:

cd ~/my-tex-project docker run --rm \ -v "$(pwd)":/workdir \ -w /workdir \ ghcr.io/texlive/texlive:latest \ latexmk -xelatex -interaction=nonstopmode main.tex

命令的每一个参数都有讲究:

  • --rm:容器跑完就删,不留垃圾容器。
  • -v "$(pwd)":/workdir:把当前目录挂载到容器的 /workdir,相当于让容器能看到你的文件。注意左边路径要写宿主机绝对路径,所以$(pwd)这个写法在 Linux/macOS 下可以直接用。
  • -w /workdir:指定容器内的工作目录为 /workdir,这样latexmk就在这个目录里生成文件。
  • -interaction=nonstopmode:编译遇到错误不弹交互式询问,直接报错退出,适合脚本调用。

Windows 用户注意,PowerShell 里$(pwd)是自动变量,可以直接写:

docker run --rm -v "${PWD}:/workdir" -w /workdir ghcr.io/texlive/texlive:latest latexmk -xelatex main.tex

如果你是 CMD 传统命令行,把${PWD}换成%cd%

编译完成后,当前目录下会多出main.pdf和一众中间文件(aux、log、fls 等)。PDF 可以直接打开,验证成功。

3.4 中文字体:最容易踩的坑

纯英文文档到这里就结束了,但中文论文还有一关——字体。容器里默认没有中文字体,直接编译 ctex 文档大概率报错或者输出方块字。

我自己处理中文字体的方式有三种,按推荐程度排列。

推荐做法:构建镜像时直接装fonts-noto-cjk,像我上面那份 Dockerfile 一样。这样镜像内部自带 Noto CJK 字体,到任何机器上都能稳定编译。

挂载方案:如果你用的是现成镜像,不想自己构建,那就把宿主机字体目录挂载进去。Linux 下执行:

docker run --rm \ -v "$(pwd)":/workdir \ -w /workdir \ -v /usr/share/fonts:/usr/share/fonts:ro \ ghcr.io/texlive/texlive:latest \ latexmk -xelatex main.tex

Windows 下可以把C:\Windows\Fonts挂载到/usr/share/fonts,macOS 则挂载/System/Library/Fonts/Library/Fonts。但这个方法有个缺点:换一台机器,字体可能就不一样了,最终编译效果略有差异。

把字体放进项目目录:在项目里建fonts/目录,把需要用到的 ttf/otf 文件放进去,然后挂载项目自身:

-v "$(pwd)":/workdir -w /workdir

LaTeX 侧用fontspec\setmainfont指定字体路径。这个方案的可复现性比挂载系统字体好,但管理字体文件比较繁琐。

我的建议是:自己构建镜像就装fonts-noto-cjk,用现成镜像就挂载系统字体。两条路都验证过,编译 ctex 模板都没问题。

3.5 日常开发的目录挂载与权限处理

上手之后你会发现,每次编译都要敲一长串docker run命令,很烦。解决方法是写一个build.sh脚本:

#!/usr/bin/env bash set -e IMAGE=ghcr.io/texlive/texlive:latest docker run --rm \ -v "$(pwd)":/workdir \ -w /workdir \ -v /usr/share/fonts:/usr/share/fonts:ro \ "$IMAGE" \ latexmk -xelatex -interaction=nonstopmode "$@"

.gitignore里加中间文件:

*.aux *.log *.out *.fls *.fdb_latexmk *.synctex.gz *.toc *.bbl *.blg

Linux 下还要注意文件属主问题。容器内默认是 root 用户,挂载目录里生成的文件属主也会变成 root,如果你之后用普通用户操作这些文件可能要加 sudo 才能删。解决方式是在docker run里指定用户:

docker run --rm -u $(id -u):$(id -g) \ -v "$(pwd)":/workdir \ -w /workdir \ ghcr.io/texlive/texlive:latest \ latexmk -xelatex main.tex

这样容器生成的文件属主就是你当前的 uid/gid,干净利落。Windows/macOS 用户通常不需要管这个,因为文件权限模型和 Linux 不一样。

4. 跟 VS Code 搭配:本地写论文,容器里编译

4.1 LaTeX Workshop 与 Docker 的结合方式

现在环境准备好了,剩下的问题是怎么舒服地写论文。VS Code 加上 LaTeX Workshop 插件是目前最流行的组合。这个插件本身支持配置外部编译工具,我们可以把默认的pdflatexxelatex换成 Docker 命令,这样在编辑器里按一下保存,PDF 就在容器里编译好了。

这个方式最大的好处是:编辑器里写代码、预览 PDF、代码补全都是宿主机上的原生体验,而真正干活(编译)的环境是固定的容器。既照顾了 IDE 的便捷,又保证了编译环境的一致性。

4.2 配置 Docker 作为编译工具

打开 VS Code 设置(Ctrl+, 或 Cmd+,),点击右上角的“打开设置(JSON)”,写入:

{ "latex-workshop.latex.tools": [ { "name": "latexmk-docker", "command": "docker", "args": [ "run", "--rm", "-v", "%DIR%:/workdir", "-w", "/workdir", "ghcr.io/texlive/texlive:latest", "latexmk", "-xelatex", "-interaction=nonstopmode", "%DOC_EXT%.tex" ] } ], "latex-workshop.latex.recipes": [ { "name": "docker latexmk", "tools": ["latexmk-docker"] } ], "latex-workshop.latex.recipe.default": "docker latexmk" }

这里有个变量要特别注意:%DIR%是当前 LaTeX 文件所在目录,%DOC_EXT%是当前文件名(不含扩展名),两个变量在 LaTeX Workshop 里都是内置的,不需要自己定义。为什么不能用%DOC%?因为%DOC%是宿主机上的完整路径,比如C:\Users\me\paper\main.tex,容器里没有这个路径,必须用相对于/workdir的文件名。

如果你的编译需要用到 bibtex,构建流程就变成“编译一次 → bibtex → 编译两次”,对应的 recipe 可以配置多个工具按顺序执行。这里我建议直接在 LaTeX 里使用latexmk,它会根据依赖自动调起 bibtex,省心得多。

配置好之后,切回.tex文件,左侧的 TeX 侧边栏里能看到“Build LaTeX project”按钮,点一下就会调用容器编译。首次编译因为要启动容器,会慢一些,之后有缓存就会快很多。

4.3 如果习惯 TeXstudio 怎么办

并不是所有人都用 VS Code,你要是习惯 TeXstudio,也可以指向 Docker。在 TeXstudio 的“选项 → 命令”里,把“XeLaTeX”命令改为:

docker run --rm -v "%.d":/workdir -w /workdir ghcr.io/texlive/texlive:latest latexmk -xelatex -interaction=nonstopmode %.tex

TeXstudio 里的%.d代表当前文件目录,%.tex代表当前文件名。设置好后,按 F5 就能在容器里编译。不过 TeXstudio 对容器工作目录的变量替换没有 VS Code 那么直观,实际操作时以你自己版本里的“命令配置”说明为准。

5. 进阶玩法:提交代码自动出 PDF

5.1 用 GitHub Actions 实现自动编译

如果论文是多人协作,或者你想在每次修改后自动拿到最新的 PDF,可以让 CI 平台来做编译。GitHub Actions 上有现成的 LaTeX action,底层就是基于 Docker 的 TeX Live 镜像。

在仓库根目录建.github/workflows/build.yml

name: Build LaTeX on: push: paths: - '**/*.tex' - '**/*.bib' - '**/*.cls' - '**/*.sty' jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: xu-cheng/latex-action@v3 with: root_file: main.tex latex_compiler: latexmk args: "-xelatex -interaction=nonstopmode" - uses: actions/upload-artifact@v4 with: name: main-pdf path: main.pdf

这个 action 内部会拉取一个 TeX Live 完整环境,所以你的仓库里不需要有任何 Dockerfile,只要把 root_file 指向主文件即可。编译完成后,PDF 会作为 artifact 上传到 Actions 页面,点击即可下载。

有一点要注意:如果仓库里的.bib文件或者自定义.cls文件有变更,也需要触发构建,所以我在paths里加上了这些扩展名。

5.2 用 GitLab CI 实现自动编译

如果你在学校/公司的 GitLab 上管理代码,流程类似,在仓库根目录建.gitlab-ci.yml

image: ghcr.io/texlive/texlive:latest compile: stage: build script: - latexmk -xelatex -interaction=nonstopmode main.tex artifacts: paths: - main.pdf expire_in: 30 days only: changes: - "**/*.tex" - "**/*.bib" - "**/*.cls" - "**/*.sty"

GitLab Runner 在对应机器上只需要有 Docker 就能跑,每次推代码,CI 自动拉镜像、自动编译、自动把 PDF 作为 artifacts 存到流水线页面。

5.3 自动编译的意义与局限

自动编译的价值不只是“不用手动敲命令”。更实际的意义是:多人协作时,每次提交都是一个可回溯的版本,PDF 和源码严格对应。以前经常出现“我这版能编译,你拉下来就不行”的问题,本质是本地环境不一致。CI 环境是干净的、固定的,编译失败一定是因为代码问题,而不是环境玄学。

这个方案也有局限:CI 默认超时时间一般 30 到 60 分钟,大论文第一次编译需要下载字体和宏包,时间较长;还有就是免费的 CI 资源有限,不适合频繁触发。我的做法是只在推送到主干分支时才触发完整编译,平时开发用本地容器编译就好。

6. 常见问题与排查技巧

6.1 Docker Desktop 起不来的两个高频原因

Windows 用户最常见的报错就是“Docker Desktop failed to start because virtualisation support wasn't detected”。这个报错的根源几乎都是虚拟化没开。

解决办法分两步。第一步,确认 BIOS/UEFI 里的虚拟化开关是否开启。Intel 平台叫 Intel VT-x,AMD 平台叫 SVM Mode,在 BIOS 里搜关键词找到后启用,重启 Windows。第二步,到“控制面板 → 程序 → 启用或关闭 Windows 功能”里,把“适用于 Linux 的 Windows 子系统”和“虚拟机平台”两项勾上,然后重启。

如果这两项都开了还不行,多半是 WSL2 内核没更新,到 PowerShell 里执行:

wsl --update wsl --set-default-version 2

再重新打开 Docker Desktop 就可以了。macOS 上如果遇到类似问题,大概率是“在设置了安全隐私选项后没有给 Docker 授权”,到系统设置里检查虚拟化框架权限即可。

6.2 镜像拉取慢 / 拉取失败怎么处理

国内网络环境拉取 Docker Hub 或 ghcr.io 镜像经常超时。两种处理方式:

第一种,配置 Docker 的 registry-mirrors。Docker Desktop 里在 Settings → Docker Engine 的 JSON 配置文件中加入:

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

保存重启后生效。镜像地址选择上优先考虑你所在云服务商提供的地址,或者学校给校内用户提供的地址,不要随便用来路不明的地址。

第二种,换镜像源。大部分 CI 平台自带的 Docker 环境会预置好镜像加速,本地如果拉取困难,可以考虑直接借用 CI 的构建产物,或者用.tar文件离线导入镜像。

提醒一下:拉取失败不要反复重试,先检查网络连通性和 DNS。我之前遇到过 ghcr.io 超时,实际是内网 DNS 解析异常导致的,把 DNS 改成公共 DNS 后就正常了。

6.3 中文乱码和缺字体

xelatex 编译中文出现方块或乱码,优先检查两件事:第一,文档类是不是用的 ctex,比如\documentclass{ctexart};第二,容器里有没有中文字体。进入容器查字体:

docker run --rm ghcr.io/texlive/texlive:latest fc-list :lang=zh

如果输出为空,说明没有中文字体。按 3.4 里的两种方案处理:挂载字体目录,或者重新构建一个带fonts-noto-cjk的镜像。另外,从 Windows 上传到服务器的源文件,要注意编码格式,统一用 UTF-8。

6.4 缺宏包、版本对不上

编译报! LaTeX Error: File 'xxx.sty' not found.最直接的方法是进容器里看这个包是否已经存在:

docker run --rm ghcr.io/texlive/texlive:latest kpsewhich xxx.sty

如果输出为空说明没装。完整版 TeX Live 镜像覆盖了绝大多数情况,万一真的缺,可以用tlmgr install xxx在容器里现场装,然后 commit 成新镜像。但这个方法有风险:下次镜像更新时,你的自定义层会被覆盖。更推荐的方式是把缺失包记录到 Dockerfile 里,用tlmgr install构建。

还有一类“版本对不上”问题,表现为在 A 电脑上能编译,在容器里报错。这时先看.log文件结尾处的错误提示,很多是宏包接口变化导致的,把相关宏包版本固定下来是最好的解决方式。

6.5 BibTeX 报错其实是正常输出

很多人在编译带参考文献的论文时,会看到类似下面的输出:

This is BibTeX, Version 0.99d (TeX Live 2022) The top-level auxiliary file: main.aux

这个信息看起来像报错,其实就是 BibTeX 在正常告诉你“我在读 main.aux,准备生成参考文献列表”。真正的问题通常出现在这之后,比如I couldn't open database file xxx.bib,那才是 bib 文件路径写错了。

如果你用的是latexmk -xelatex,它会在编译过程中自动调用 BibTeX,不需要手动分步执行。如果分步操作,记住顺序:xelatex → bibtex → xelatex → xelatex,少一步参考文献都会对不齐。

6.6 LaTeX 高频小问题速查表

写论文过程中,有些细节点几乎每天都会用到,我用一个小表把最常见的几个列出来:

需求LaTeX 写法
换行\\\newline
希腊字母\alpha\beta\gamma\sigma
行内公式$...$,比如$a^2+b^2=c^2$
独立公式\[...\]equation环境
插入图片\includegraphics[width=0.8\textwidth]{fig.png},需配合graphicx宏包
表格tabular环境
引用参考文献\cite{key},配合.bib文件
生成目录\tableofcontents
强调文字\emph{内容}

这些语法在容器编译和本地编译上没有任何区别,它们只是 LaTeX 宏包的语法问题。

一些个人的体会

说实话,我一开始也不习惯用容器写论文,总觉得多绕了一层,不如直接装个 TeX Live 来得直接。但用了一段时间之后,反而是这种“隔离”让我最安心。不管换了新电脑,还是帮别人跑模板,不需要再关心对方系统里装了什么东西,Docker 镜像拉到哪,编译结果就在哪。对我这种经常在几台机器之间切换的人来说,这种确定性比省那几分钟环境安装时间重要得多。

如果你决定尝试,我的建议是从小处开始:先拉一个完整版现成镜像,参照第 3 章的命令把第一篇论文编译出来,再逐步加上字体、VS Code 配置、自动编译。这个过程不会花超过半天,但以后写论文时,那种“环境又出问题”的焦虑感会彻底消失。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询