Voltar ao blog

Automate IPYNB to PDF Conversion: A CI/CD Guide with GitHub Actions

Stop converting notebooks by hand. Automate IPYNB to PDF conversion in CI/CD with GitHub Actions, GitLab CI, and pre-commit hooks. Includes a copy-pasteable workflow file.

  • #automation
  • #ci-cd
  • #devops

If you're converting the same notebooks to PDF every week — for reports, documentation, or homework submissions — you're wasting time. Automation turns this into a zero-touch process: push a notebook, get a PDF back, move on with your day.

This guide covers three automation paths, from simplest to most powerful.

Path 1 — Pre-commit Hook (Local Automation)

The lightest-weight option. Every time you commit a notebook, a hook regenerates its PDF.

Install pre-commit:

pip install pre-commit

Create .pre-commit-config.yaml in your repo:

repos:
  - repo: local
    hooks:
      - id: ipynb-to-pdf
        name: Convert notebooks to PDF
        entry: jupyter nbconvert --to webpdf
        language: system
        files: \.ipynb$
        pass_filenames: true

Install once:

pre-commit install

Now every git commit that includes a .ipynb will regenerate its PDF. The catch: you need nbconvert + Playwright installed locally. See our LaTeX-free guide for setup.

Path 2 — GitHub Actions (Cloud Automation)

The most popular path for teams. Push a notebook, GitHub converts it to PDF and uploads it as an artifact.

Minimal Workflow

Create .github/workflows/notebook-pdf.yml:

name: Convert Notebooks to PDF

on:
  push:
    paths:
      - 'notebooks/*.ipynb'
  pull_request:
    paths:
      - 'notebooks/*.ipynb'
  workflow_dispatch: # Manual trigger

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

      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'

      - name: Install nbconvert + Playwright
        run: |
          pip install "nbconvert[webpdf]"
          playwright install chromium --with-deps

      - name: Convert notebooks
        run: |
          mkdir -p pdfs
          for nb in notebooks/*.ipynb; do
            jupyter nbconvert --to webpdf "$nb" --output-dir pdfs/
          done

      - name: Upload PDFs
        uses: actions/upload-artifact@v4
        with:
          name: notebook-pdfs
          path: pdfs/*.pdf
          retention-days: 30

Push a notebook, and within ~60 seconds GitHub will produce a downloadable PDF artifact.

Why webpdf instead of --to pdf?

LaTeX in CI is a nightmare. The texlive-xetex install alone is 1+ GB and adds 3 minutes to every build. webpdf uses headless Chromium (~200 MB) and finishes in seconds.

Executing Notebooks During Conversion

If your notebooks don't have outputs saved, add --execute:

- name: Convert notebooks with execution
  run: |
    mkdir -p pdfs
    for nb in notebooks/*.ipynb; do
      jupyter nbconvert --to webpdf --execute "$nb" --output-dir pdfs/
    done
  env:
    MPLBACKEND: Agg # matplotlib headless mode

You'll need to install your notebook's dependencies (pip install -r requirements.txt) before this step.

Auto-committing PDFs Back to the Repo

If you want PDFs version-controlled alongside notebooks:

- name: Commit PDFs
  run: |
    git config user.name "github-actions[bot]"
    git config user.email "github-actions[bot]@users.noreply.github.com"
    git add pdfs/*.pdf
    git commit -m "Auto-update PDFs [skip ci]" || echo "No changes"
    git push

Use [skip ci] in the commit message to avoid an infinite loop.

Path 3 — GitLab CI

The same workflow in GitLab's syntax (.gitlab-ci.yml):

stages:
  - convert

convert-notebooks:
  stage: convert
  image: python:3.12-slim
  before_script:
    - apt-get update && apt-get install -y --no-install-recommends
        libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 libcups2
        libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3
        libxrandr2 libgbm1 libpango-1.0-0 libcairo2 libasound2
    - pip install "nbconvert[webpdf]"
    - playwright install chromium
  script:
    - mkdir -p pdfs
    - for nb in notebooks/*.ipynb; do
        jupyter nbconvert --to webpdf "$nb" --output-dir pdfs/;
      done
  artifacts:
    paths:
      - pdfs/*.pdf
    expire_in: 30 days

Path 4 — Scheduled Reports (Cron)

If you want PDFs generated on a schedule — say, a weekly report from a notebook that pulls fresh data:

on:
  schedule:
    - cron: '0 8 * * 1' # Every Monday 08:00 UTC
  workflow_dispatch:

jobs:
  weekly-report:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'
      - run: pip install -r requirements.txt "nbconvert[webpdf]"
      - run: playwright install chromium --with-deps
      - run: jupyter nbconvert --to webpdf --execute reports/weekly.ipynb --output-dir .
      - name: Send to Slack
        uses: slackapi/slack-github-action@v1
        with:
          slack-message: "Weekly report PDF attached"
        # ...configure webhook

Advanced Tips

Caching Python + Playwright

Re-downloading Chromium on every run wastes 30 seconds. Cache it:

- uses: actions/cache@v4
  with:
    path: |
      ~/.cache/ms-playwright
      ~/.cache/pip
    key: ${{ runner.os }}-playwright-${{ hashFiles('requirements.txt') }}

Using the Online Converter from CI

If you don't want to maintain a Python toolchain in CI at all, you can POST to an online converter's API:

- name: Convert via API
  run: |
    curl -X POST https://ipynbtopdf.org/api/convert \
      -F "file=@notebooks/report.ipynb" \
      -o pdfs/report.pdf

This is dramatically simpler — no Python, no Playwright, no Chromium. Just check the API's rate limits and authentication requirements.

Handling Failures Gracefully

Notebook execution can fail (missing data, API outages). Don't let one bad notebook block the rest:

for nb in notebooks/*.ipynb; do
  echo "Converting $nb..."
  jupyter nbconvert --to webpdf --execute "$nb" --output-dir pdfs/ \
    || echo "WARNING: $nb failed, continuing"
done

Performance Comparison

MethodCold buildWarm buildOutput size
nbconvert --to pdf + LaTeX~4 min~3 min~500 MB
nbconvert --to webpdf~90s~30s~250 MB
Online converter via curl~10s~10s~0 MB

The online-converter-via-curl path is fastest by an order of magnitude if you can use it.

Conclusion

Automating IPYNB to PDF conversion is one of those small changes that pays off forever. Whether you start with a pre-commit hook for local work, a GitHub Actions workflow for team automation, or a scheduled cron job for recurring reports, the principle is the same: let the computer do the repetitive work. Your future self will thank you the next time a notebook update automatically produces a fresh PDF without you touching anything.