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
| Method | Cold build | Warm build | Output 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.