Controlling Read the Docs Builds with .readthedocs.yaml: Python Versions, Dependencies, and PDF Output
Pin your Read the Docs build environment, dependencies, and PDF output with a .readthedocs.yaml file — including a working example, LaTeX pitfalls, and the platform limits you cannot configure away.
08 Dec 2025, 09:58 UTC

If your Read the Docs builds are picking the wrong Python version, missing dependencies, or never producing the PDF you expected, the fix is almost always a .readthedocs.yaml file at the root of your repository. Without it, Read the Docs guesses at your environment; with it, you pin the operating system, toolchain, install steps, and output formats explicitly. This guide explains what the file controls, walks through a working configuration, and covers the limits and mistakes that most often break builds.
What the config file actually does
Read the Docs triggers a build whenever you push to a configured branch. The .readthedocs.yaml file (version 2 is the current schema) is read at the start of that build and determines three things: the build image and language tools, the commands used to install dependencies and build the docs, and which output formats (HTML, PDF, ePub) are produced.
Two platform behaviors matter before you write anything:
- The file must be committed to a branch that Read the Docs builds — typically your default branch, or any branch configured as an active version. Editing it on an unconfigured feature branch has no effect.
- The file only affects builds triggered after the commit. Existing builds are not retroactively changed.
A worked configuration
Here is a representative configuration for a Sphinx project with a requirements.txt at the repository root and sources under docs/:
version: 2
build:
os: ubuntu-22.04
tools:
python: "3.11"
commands:
- pip install -r requirements.txt
- sphinx-build -b html docs _build/html
formats:
- pdf
Walking through it:
version: 2selects the current configuration schema. Version 1 is deprecated; always use 2.build.osandbuild.tools.pythonpin the build image and interpreter. Pinning matters: if you leave the Python version unspecified, Read the Docs picks a default that may not match what you test against locally, and that default can change over time.build.commandsruns a fully custom build. The first line installs your Python dependencies; the second invokes Sphinx directly. Note that when you usecommands, you take over the whole build — Read the Docs will not run its own Sphinx invocation for you.formatsrequests a PDF in addition to the default HTML output.
If you do not need custom commands, the higher-level alternative is shorter and lets Read the Docs handle the Sphinx invocation:
version: 2
build:
os: ubuntu-22.04
tools:
python: "3.11"
sphinx:
configuration: docs/conf.py
python:
install:
- requirements: requirements.txt
formats:
- pdf
Prefer this form unless you have a genuine reason to script the build yourself (for example, running a pre-processing step before Sphinx). It is less code to maintain and benefits from platform improvements to the standard build path.
Getting the PDF to actually build
Enabling pdf in formats is the easy half. PDF generation from Sphinx goes through LaTeX, which means your conf.py must have sane LaTeX settings and your content must be LaTeX-compatible. Common failure causes:
- Extensions or raw HTML directives that have no LaTeX equivalent. Guard HTML-only content with
onlydirectives, e.g... only:: html. - Images in formats LaTeX cannot embed. Stick to PNG, JPEG, or PDF images.
- Unicode characters outside the default LaTeX font coverage. Setting
latex_engine = "xelatex"inconf.pyusually resolves this.
Before pushing, test locally with sphinx-build -b latex docs _build/latex and then run pdflatex (or xelatex) inside _build/latex. You need a local TeX installation for this. If the PDF builds locally but fails on Read the Docs, compare the Sphinx and Python versions in the build log against your local environment — a version skew is the usual culprit.
Platform limits you cannot configure away
The YAML file controls your build, but it cannot override platform caps:
- Build time. Builds are terminated after a fixed limit (historically around 60 minutes on the community site). Heavy dependency trees — anything compiling large C extensions from source — can hit this. Mitigate by pinning wheels-friendly dependency versions or trimming requirements.
- Memory and artifact size. Very large documentation sets or huge generated assets can exceed memory or storage limits, and the build is killed without a graceful error. If this happens, split the docs or reduce asset sizes.
- Unknown keys are ignored silently. A misspelled key like
format:instead offormats:will not produce an error; the setting just does nothing. This makes typos hard to spot — always confirm behavior in the build log rather than assuming the file was applied.
Common mistakes
- Bad indentation. YAML is whitespace-sensitive. A mis-indented
tools:block silently changes the structure. Validate the file with any YAML parser before committing. - Wrong requirements path. Paths in the config are relative to the repository root, not to the YAML file's directory or the docs folder. If your requirements live in
docs/requirements.txt, say so explicitly. - Expecting environment variables to persist. Variables set inside one build command do not automatically appear in later steps or in the deployed site; they exist only within the build process.
- Using
build.commandsand wondering why formats vanished. With a fully custom command list, you are responsible for producing output in the locations Read the Docs expects. If artifacts go missing, check that your commands write to the documented output directories.
Verifying the configuration took effect
After committing and pushing, trigger a build (a new push, or the "Build version" button in the dashboard) and check three things:
- Open the build log in the Read the Docs dashboard. The early lines show the selected OS image and Python version — confirm they match your YAML.
- Confirm each of your commands appears in the log and exits successfully.
- On the version's downloads page, confirm the PDF artifact exists and opens correctly.
If the log shows a different Python version than you specified, the config file was not picked up — check that it is named exactly .readthedocs.yaml, sits at the repository root, and is on the branch being built.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.