Docker / CI での導入

手元のディスクに展開された TeX Live 2024 は 8.7 GB あります。ところが Docker Hub が公開している texlive/texlive:latest は圧縮後 約 2.5 GB、最小構成の latest-minimal なら 約 335 MB(いずれも 2026 年 8 月時点の amd64 の表示値)。LaTeX を Docker で動かすという選択は、まずこの数字の話です。このページは texlive/texlive イメージそのもの——誰が作り、中に何が入り、どのタグを選び、どうやってバージョンを固定するか——と、コンテナならではの落とし穴(生成された PDF が root 所有になる件)を扱います。GitHub Actions のワークフローの書き方そのものは CI のページが持っています。

なぜ TeX をコンテナで動かすのか

理由は一つ、再現性 です。同じ .tex から同じ PDF が出るかどうかは、原稿だけでは決まりません。TeX Live が何年版か、各パッケージがどのリビジョンか、どのフォントが登録されているかに左右されます。Docker イメージは、その環境を OS ごと固めて配る箱 です。中には特定スナップショットの TeX Live・エンジン・パッケージ・フォントが入っていて、手元が Windows でも macOS でも、CI のランナー上でも、同じ箱を開ければ同じ環境が立ち上がります。「自分の PC では通るのに共著者や CI では失敗する」が起きにくくなるのはそのためです。

コンテナと手元のインストールは排他ではありません。日々の執筆はエディタとローカルの TeX Live で行い、最終ビルドと共著者への配布だけ をコンテナに任せる、という併用が現実的です。手元の環境を汚さずにいろいろ試せるのも利点で、たとえば \usepackage{...} が通らない原因を切り分けたいときに、まっさらな TeX Live を数分で用意できます。ただし対価もあります。数 GB のイメージを一度は引く必要があり、コンテナ内では GUI のビューアも SyncTeX の逆検索も動きません。コンテナは編集環境ではなくビルド環境 と割り切るのが素直です。

texlive/texlive イメージは誰が作っているのか

作っているのは TeX Users Group ではなく、Island of TeX というコミュニティです(TeX Live の公式イメージではありません)。同じイメージが Docker Hub の texlive/texlive と GitLab の registry.gitlab.com/islandoftex/images/texlive の両方から取得できます。土台は Debian testing の slim イメージ。Dockerfile には Alpine を選ばなかった理由が書かれていて、作成当時 biber などのバイナリが Linux/musl 向けに配布されていなかったから、とあります。中身は TeX Live 本体だけではありません。arara のための Java、biberxindy のための Perl、minted のための Python と Pygments、EPS 変換のための Ghostscript、pgfplots のための gnuplot までまとめて入っています。

terminal
# One-off build of ./main.tex. The image's own WORKDIR is /workdir.
docker run --rm -v "$PWD":/workdir -w /workdir texlive/texlive \
  latexmk -pdf main.tex

# Poke around inside instead of building:
docker run --rm -it -v "$PWD":/workdir -w /workdir texlive/texlive bash

--rm は終了後にコンテナを捨て、-v "$PWD":/workdir は現在地をコンテナ内の /workdir に結び付け、-w でそこを作業ディレクトリにします。入力も出力も手元のフォルダにあるので、PDF はその場に落ちます。日本語なら LuaLaTeX が最短で、latexmk -lualatex main.tex に差し替えるだけ。upLaTeX と dvipdfmx の経路を使うなら、後述の .latexmkrc を原稿の横に置いてから latexmk main.tex を呼びます。なお latest はイメージの中で tlmgr update --self --all を実行して更新できますが、コンテナを捨てれば元に戻る点は覚えておいてください。

どのタグを選ぶか —— scheme とサイズの実測

タグは scheme(規模)doc / src の有無 の掛け算になっています。scheme は minimal / basic / small / medium / full の 5 段階(ConTeXt 用の context を含めれば 6 種)で、無印の latestlatest-full の別名です。そこに -doc(マニュアル付き)、-src.dtx 等のソース付き)、-doc-src(両方)を組み合わせます。効き方は劇的で、Docker Hub の表示では latest(= full)が約 2.5 GB なのに対し、latest-doc-src は約 6.7 GB——ドキュメントを足すだけで 2 倍以上 になります。前ページで見たとおり texmf-dist/doc だけで 3.7 GB あるので、当然といえば当然です。

タグ中身Docker Hub の表示サイズ(amd64, 2026年8月)
latestlatest-full の別名。全パッケージ、文書・ソースなし約 2.53 GB — 迷ったらこれ
latest-mediummedium scheme。日常的な文書はだいたい通る約 898 MB
latest-smallsmall scheme。CI を軽くしたいとき約 590 MB
latest-basicbasic scheme。素の LaTeX + わずかな追加約 367 MB
latest-minimalminimal scheme。plain TeX 相当で最小約 335 MB
latest-doc-srcfull + マニュアル + ソース。texdoc も引ける約 6.69 GB — 手元での調べ物向け
TL2018-historic過去リリース。2013 年以降が用意されている古い原稿の再ビルド用。下の注意も参照

実務の選び方は単純です。CI では latest(無印)か、必要なパッケージが入るなら latest-medium 手元で texdoc を引きたいときだけ -doc 系を取ります。scheme を小さくしたときの注意が一つあり、Island of TeX の README によれば、full 以外のイメージで tlmgr install により新しい実行ファイルを入れても 自動では PATH に載りませんtlmgr install <pkg> && tlmgr path add のように続けて実行する必要があります。ARMv8(arm64)向けのビルドも提供されていますが、README は実験的な位置づけだと明記しているので、Apple Silicon で妙な挙動に当たったら --platform linux/amd64 を試す価値があります。

「historic タグなら不変」ではない —— digest まで固定する

過去リリースのタグでも、指しているイメージは変わります。 Island of TeX の README は historic イメージについて「毎月再ビルドされ、土台の OS イメージに更新があれば更新される」と明言しています。TeX Live 側は凍結されていても、Debian 側のライブラリは動くわけです。加えて latest は週次で新しい CTAN スナップショットへ移り、その週次ビルドには TL{リリース}-{年}-{月}-{日} 形式のタグが付きます(TL2021 より前は時分まで含む形式でした)。だから 論文の最終ビルドを本当に固定したいなら、タグではなく digest を書く のが唯一確実な方法です。

terminal
# A tag can be re-pointed later; a digest cannot.
docker pull texlive/texlive:latest
docker image inspect --format '{{index .RepoDigests 0}}' texlive/texlive:latest
# -> texlive/texlive@sha256:0123abcd...

# Record that string in the repository and build against it forever:
docker run --rm -v "$PWD":/workdir -w /workdir \
  texlive/texlive@sha256:0123abcd... latexmk -pdf main.tex

# Belt and braces for archival work: keep your own copy of the image.
docker save texlive/texlive@sha256:0123abcd... | gzip > texlive-frozen.tar.gz

digest はイメージの内容そのもののハッシュなので、リモート側で何が起きても同じものしか取れません。学位論文や、数年後に査読者から再現を求められうる仕事では、この一行をリポジトリに書いておくだけで将来の自分が救われます。逆に、そこまでの厳密さが要らない日常のビルドでは latest で構いません。「どちらでもよい」ではなく「何のためのビルドか」で選ぶ、というのがここでの判断基準です。

出力された PDF が root 所有になる問題

Linux で最初にぶつかるのがこれです。イメージの既定ユーザーは root ——Island of TeX の README も「apt で追加パッケージを入れやすいように既定ユーザーは root にしてある」と明記しています——なので、バインドマウントしたディレクトリに書き出された main.pdfmain.aux は root 所有になり、そのあとエディタから保存できない、git clean で消せない、といったことが起きます。macOS と Windows の Docker Desktop はファイル共有層が所有者を書き換えるので表面化しませんが、Linux と CI では日常的に踏みます。対処は --user で自分の UID と GID を渡すことです。

terminal
# Run as the calling user, so the PDF belongs to you.
docker run --rm --user "$(id -u):$(id -g)" \
  -v "$PWD":/workdir -w /workdir \
  texlive/texlive latexmk -pdf main.tex

# If a package writes to $HOME (luaotfload caches, fontconfig), give it one:
docker run --rm --user "$(id -u):$(id -g)" -e HOME=/workdir \
  -v "$PWD":/workdir -w /workdir \
  texlive/texlive latexmk -lualatex main.tex

--user を渡すと、こんどはホームディレクトリが無いユーザーとして走ることになり、キャッシュを書こうとする LuaTeX や fontconfig が文句を言う場合があります。そのときは上のように HOME を書き込めるディレクトリに向けてやれば済みます。なお 2025 年 2 月以降、イメージには texlive という非 root ユーザーが用意されていて、README は自作イメージから使うときはこれを使うよう勧めています(ただし README 自身が「バインドマウントの権限は調整が要るかもしれない」と付け加えています)。根本的には、ホスト側とコンテナ側で同じ UID を使うのが唯一の解 です。

プロジェクト専用のイメージを作る

毎回 tlmgr install するくらいなら、必要なものを入れたイメージを一度作って固定する ほうが速く、失敗の原因も追いやすくなります。FROM texlive/texlive:latest-medium から始めて、足りないパッケージやフォントを足すだけです。ここで一つ面白い仕掛けがあります。Island of TeX のイメージは、texlive-local という 架空の Debian パッケージ(バージョン番号は 9999.99999999)を equivs で作って登録済みにしてあります。おかげで apt-get install pandoc のように TeX Live に依存する Debian パッケージを入れても、Debian 版の TeX Live が二重に降ってこない のです。

terminal
# Dockerfile
FROM texlive/texlive:latest-medium

# Extra TeX packages. On a non-full scheme, "path add" is required
# for any package that installs a new executable.
RUN tlmgr install latexmk siunitx biblatex biber \
 && tlmgr path add

# Extra system packages: the image already claims texlive is installed,
# so this will not pull in Debian's own TeX Live.
RUN apt-get update \
 && apt-get install -y --no-install-recommends fonts-noto-cjk \
 && rm -rf /var/lib/apt/lists/*

WORKDIR /workdir

docker build -t myproject-tex . で焼いたら、あとはこのタグを使うだけです。日本語の CJK フォントを入れておけば、LuaLaTeX と luatexja-fontspec の組み合わせが箱の中で完結します。イメージを共著者やチームに配るなら、社内レジストリに push するか、docker save で固めて渡すのが確実です。ここまで来ると「TeX Live を各自で入れてもらう」よりも 依頼が一行で済む のが最大の利点になります。

CI から使うとき —— GitHub Actions と GitLab CI

GitLab CI ではジョブの image: に書くだけ、GitHub Actions ではコンテナを直接指定するか xu-cheng/latex-action を使います。ここで一つ、思い込みやすい点を潰しておきます。xu-cheng/latex-action が使うのは texlive/texlive ではありません。 このアクションは同じ作者の xu-cheng/latex-docker が公開する ghcr.io/xu-cheng/texlive-alpineghcr.io/xu-cheng/texlive-debian を引きます。os 入力の既定値が alpine なのはそのためで、texlive_version には 2020 から 2026、または latest を指定できます。つまり 同じ「TeX Live 入りコンテナ」でも、経路によって中身は別物 です。

terminal
# .gitlab-ci.yml - the image goes straight into the job.
build:
  image: texlive/texlive:latest
  script:
    - latexmk -pdf main.tex
  artifacts:
    paths:
      - main.pdf

# GitHub Actions, run inside the same image instead of an action:
jobs:
  build:
    runs-on: ubuntu-latest
    container: texlive/texlive@sha256:0123abcd...
    steps:
      - uses: actions/checkout@v7
      - run: latexmk -pdf main.tex

container: にイメージを書く形は、ローカルの docker run と CI が 文字どおり同じ環境 になるのが利点です。逆に xu-cheng/latex-action を使う形は、記述が短く、latexmk_use_lualatexlatexmk_shell_escape といった入力で細かく制御できるのが利点です(既定の latexmk 引数は -pdf -file-line-error -halt-on-error -interaction=nonstopmode)。どちらを選ぶかは「手元と CI の同一性」対「記述の短さ」のトレードオフ で、再現性を最優先するなら前者です。ワークフローの組み立て、キャッシュ、成果物の扱いといった CI 側の作法は CI のページが詳しく扱っています。

コンテナに渡しやすい原稿にする

コンテナでこける原稿には共通点があります。ビルドの手順がエディタの設定にしか書かれていない ことです。ルートファイル、エンジン、補助コマンド、出力先——この四つがリポジトリの中の文字として存在していれば、手元でも Docker でも CI でも同じ一行 latexmk main.tex で済みます。日本語で upLaTeX と dvipdfmx を使うなら、main.tex の横に .latexmkrc を置いて経路を明示してください。$pdf_mode = 3 は「DVI を作ってから dvipdfmx で PDF にする」という指定です。

latex
# .latexmkrc: upLaTeX + dvipdfmx
$latex = "uplatex -interaction=nonstopmode -halt-on-error %O %S";
$bibtex = "upbibtex %O %B";
$dvipdf = "dvipdfmx %O -o %D %S";
$pdf_mode = 3;

この .latexmkrc をコミットしてしまえば、docker run・GitLab CI・GitHub Actions のどれから呼んでも同じ規則で PDF が作られます。あわせて \usepackage するパッケージを実際に増やしたら、イメージのタグも見直す 習慣をつけてください。scheme を medium に絞ったまま tikz-cd を足すと、ある日突然 ! LaTeX Error: File 'tikz-cd.sty' not found. で CI だけが落ちます。原稿と環境は一緒に版管理する——コンテナで組版するというのは、結局そういう約束です。