FormaTeX

\usepackage{gitlab-ci}

Compile LaTeX in GitLab CI/CD

Automate LaTeX compilation in your GitLab pipeline. Every push and merge request compiles your documents, catches errors early, and publishes PDFs as GitLab artifacts — with no TeX Live installation in your runner.

\section{Why CI/CD for LaTeX}

Treat documents like code

Automated builds catch broken packages and compilation errors before they reach collaborators or reviewers. PDFs are always a pipeline run away.

Catch errors before merge

Pipeline checks fail if the document doesn't compile. Reviewers see broken LaTeX before it lands on main.

Artifacts on every run

GitLab stores compiled PDFs as job artifacts. Download the latest build directly from the pipeline UI.

No local TeX Live needed

Contributors edit .tex files without installing a 4 GB TeX Live distribution. Compilation happens in the cloud.

\section{Prerequisites}

Before you start

  • FormaTeX account — free tier works (15 compilations/month)
  • A FormaTeX API key from your dashboard
  • A GitLab repository containing .tex files

Setting the CI/CD variable

In GitLab, go to Settings › CI/CD › Variables › Add variable. Set the key to FORMATEX_API_KEY, paste your API key as the value, and enable Masked so it never appears in job logs.

\section{Basic pipeline}

Your first .gitlab-ci.yml

Drop this file at the root of your repository. It pulls alpine:latest, installs curl and jq, compiles main.tex, and stores the resulting PDF as a GitLab artifact for one week.

# .gitlab-ci.yml
stages:
  - build

compile-latex:
  stage: build
  image: alpine:latest
  before_script:
    - apk add --no-cache curl jq
  script:
    - |
      curl -X POST https://api.formatex.io/v1/compile/sync \
        -H "Authorization: Bearer $FORMATEX_API_KEY" \
        -H "Content-Type: application/json" \
        -d "{\"source\": $(cat main.tex | jq -Rs .), \"engine\": \"pdflatex\"}" \
        --output output.pdf
  artifacts:
    paths:
      - output.pdf
    expire_in: 1 week
  only:
    - main
    - merge_requests

\section{Advanced pipeline}

YAML anchors and parallel jobs

Scale to multiple documents without repeating yourself. This pipeline uses a YAML anchor to define a reusable compile template, then instantiates it formain.texandpresentation.texin parallel. The pages job then publishes all PDFs to GitLab Pages.

# .gitlab-ci.yml — advanced with matrix and error handling
stages:
  - validate
  - build
  - publish

variables:
  FORMATEX_API_URL: "https://api.formatex.io/v1"

.compile-template: &compile
  image: alpine:latest
  before_script:
    - apk add --no-cache curl jq
  script:
    - |
      RESPONSE=$(curl -s -w "\n%{http_code}" \
        -X POST "$FORMATEX_API_URL/compile/sync" \
        -H "Authorization: Bearer $FORMATEX_API_KEY" \
        -H "Content-Type: application/json" \
        -d "{\"source\": $(cat $TEX_FILE | jq -Rs .), \"engine\": \"$ENGINE\"}")
      
      HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
      if [ "$HTTP_CODE" != "200" ]; then
        echo "Error: $(echo "$RESPONSE" | head -n-1)"
        exit 1
      fi
      echo "$RESPONSE" | head -n-1 > "$OUTPUT_FILE"
  artifacts:
    paths:
      - "*.pdf"
    expire_in: 30 days

compile-main:
  <<: *compile
  stage: build
  variables:
    TEX_FILE: main.tex
    ENGINE: pdflatex
    OUTPUT_FILE: main.pdf

compile-presentation:
  <<: *compile
  stage: build
  variables:
    TEX_FILE: presentation.tex
    ENGINE: xelatex
    OUTPUT_FILE: presentation.pdf

pages:
  stage: publish
  script:
    - mkdir -p public
    - cp *.pdf public/
  artifacts:
    paths:
      - public
  only:
    - main

\section{GitLab Pages}

Publish PDFs via GitLab Pages

The pages job in the advanced pipeline copies all compiled PDFs into the public/ directory and exposes them as a GitLab Pages site. The compiled documents are then available at https://<group>.gitlab.io/<project>/ automatically on every push to main. No manual uploads, no hosting setup.

\section{Troubleshooting}

Common issues and solutions

curl: command not found

Alpine images ship without curl by default. Always include apk add --no-cache curl jq in your before_script. If you switch to a Debian-based image, use apt-get install -y curl jq instead.

API key not masked in logs

Make sure you created the variable with the Masked option enabled in GitLab Settings › CI/CD › Variables. Without masking, the key appears in plain text in job logs. Re-create the variable with Masked enabled.

Large .tex files timing out

The sync endpoint has a timeout for very large documents. For files over 10 MB, switch to the async endpoint (/v1/compile/async) which returns a job ID. Poll /v1/jobs/{id} until the status is complete, then download the PDF.

\section{Related integrations}

Use FormaTeX from any environment

FormaTeX is a plain HTTP API. Copy-paste examples for every major platform.

Ready to automate your LaTeX builds?

Free tier — 15 compilations per month. No credit card required.

One quick thing

We track anonymous usage — page views, feature usage, compilation events — to understand what works and what doesn't. No ads, no personal data, no third-party sharing.

Cookie policy