> ## Documentation Index
> Fetch the complete documentation index at: https://resources.latex-cloud-studio.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Automating LaTeX Builds - latexmk, Makefiles, and CI

> Stop compiling by hand. Reproducible LaTeX builds with latexmk, a minimal Makefile, and GitHub Actions CI that compiles your paper on every push.

A LaTeX document with citations needs up to four compiler passes in the right order — and "run it again until the warnings stop" is not a build system. This guide sets up real automation in three steps: `latexmk` locally, a Makefile for project-specific tasks, and CI that compiles the paper on every push so a broken build can never hide until the night before a deadline.

## Step 1: `latexmk` — the Standard Nobody Told You About

`latexmk` ships with TeX Live and solves the multi-pass problem completely: it runs pdfLaTeX, BibTeX/biber, and cross-reference passes as often as needed, in the right order, until the document is stable.

```bash theme={null}
# One command, correct number of passes, every time:
latexmk -pdf main.tex

# Watch mode: recompile automatically on every save
latexmk -pdf -pvc main.tex

# Clean up generated files
latexmk -c
```

Per-project configuration goes in a `.latexmkrc` file next to the document:

```perl theme={null}
# .latexmkrc — project build configuration
$pdf_mode = 1;           # build PDF via pdflatex
$bibtex_use = 2;         # run bibtex/biber as needed
@default_files = ('main.tex');
$out_dir = 'build';      # keep generated files out of the source tree
```

If you take one thing from this post: stop calling `pdflatex` by hand and use `latexmk`. Everything below builds on it.

## Step 2: A Makefile for the Project-Specific Parts

`latexmk` handles compilation; a Makefile handles everything *around* it — the diff PDF for reviewers, the arXiv bundle, the word count:

```make theme={null}
# Makefile — the tasks latexmk doesn't know about
MAIN = main

.PHONY: pdf diff arxiv clean

pdf:
	latexmk -pdf $(MAIN).tex

# Marked-up PDF against the submitted version (tag: submitted-v1)
diff:
	latexdiff-vc --git -r submitted-v1 --pdf $(MAIN).tex

# Self-contained bundle with the flattened bibliography
arxiv: pdf
	tar czf arxiv.tar.gz $(MAIN).tex $(MAIN).bbl figures/

clean:
	latexmk -C
	rm -f arxiv.tar.gz
```

Now `make diff` answers the reviewer question "what changed since submission?" in one command, and `make arxiv` produces the upload bundle without a checklist. The `$(MAIN).bbl` in the arXiv target matters: arXiv compiles your source but does not run BibTeX, so the generated `.bbl` must ship inside the bundle.

## Step 3: CI — Compile on Every Push

With the project in Git, a small GitHub Actions workflow compiles the paper on every push and fails loudly when someone commits a broken reference or a missing figure:

```yaml theme={null}
# .github/workflows/build.yml
name: Build paper

on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Compile LaTeX
        uses: xu-cheng/latex-action@v3
        with:
          root_file: main.tex
          latexmk_use_lualatex: false

      - name: Upload PDF
        uses: actions/upload-artifact@v4
        with:
          name: paper
          path: main.pdf
```

The payoff compounds with co-authors: the PDF artifact on every commit means nobody needs a local TeX installation to check the current state, and a red ✗ on a pull request catches the broken build at commit time — not at deadline time.

Two habits that keep CI useful:

* **Treat warnings you care about as errors.** `latexmk` returns non-zero on errors, but undefined references are only warnings by default — grep the log in CI (`grep -q "undefined references" && exit 1`) if you want them to fail the build.
* **Pin your TeX environment.** The action above uses a containerized TeX Live; avoid "latest" drift by pinning the action version, so this year's paper still compiles next year.

## Where the Browser Editor Fits

Automation and a cloud editor are not competing choices — they meet in the repository. With [GitHub sync](/product/github-sync-import-export) the same repo that CI compiles is the project your co-authors edit in the browser: writers who never touch a terminal work in the editor, the Makefile-and-CI machinery runs against every push, and Git remains the single source of truth for both.

**[Connect a repository and try it →](https://app.latex-cloud-studio.com/?utm_source=resources\&utm_medium=inline_link\&utm_campaign=docs_open_app\&utm_content=blog_latex_automation_ci_primary_cta)**

## Related Guides

* **[LaTeX Collaboration Best Practices](/blog/latex-collaboration-best-practices)** - The conventions that make the Git flow calm
* **[Bibliography Management](/blog/latex-bibliography-management)** - Why those extra passes exist at all
* **[Common LaTeX Errors](/blog/common-latex-errors-fixes)** - What to do when CI goes red
