How to create reproducible builds
Your documentation depends on a number of dependencies to be built. If your docs don’t have reproducible builds, an update in a dependency can break your builds when least expected, or make your docs look different from your local version. This guide will help you to keep your builds working over time, so that you can focus on content.
Use a .readthedocs.yaml
configuration file
We recommend using a configuration file to manage your documentation. Our config file provides you per version settings, and those settings live in your Git repository.
This allows you to validate changes using pull requests, and ensures that all your versions can be rebuilt from a reproducible configuration.
Use a requirements file for Python dependencies
We recommend using a Pip requirements file or Conda environment file to pin Python dependencies. This ensures that top-level dependencies and extensions don’t change.
A configuration file with explicit dependencies looks like this:
version: 2
build:
os: "ubuntu-22.04"
tools:
python: "3.12"
# Build from the docs/ directory with Sphinx
sphinx:
configuration: docs/conf.py
# Explicitly set the version of Python and its requirements
python:
install:
- requirements: docs/requirements.txt
# Defining the exact version will make sure things don't break
sphinx==5.3.0
sphinx_rtd_theme==1.1.1
readthedocs-sphinx-search==0.1.1
Tip
Remember to update your docs’ dependencies from time to time to get new improvements and fixes. It also makes it easy to manage in case a version reaches its end of support date.
Pin your transitive dependencies
Once you have pinned your own dependencies, the next things to worry about are the dependencies of your dependencies. These are called transitive dependencies, and they can upgrade without warning if you do not pin these packages as well.
We recommend pip-tools to help address this problem.
It allows you to specify a requirements.in
file with your top-level dependencies,
and it generates a requirements.txt
file with the full set of transitive dependencies.
- ✅ Good:
All your transitive dependencies are defined, which ensures new package releases will not break your docs.
sphinx==5.3.0
# # This file is autogenerated by pip-compile with Python 3.10 # by the following command: # # pip-compile docs.in # alabaster==0.7.12 # via sphinx babel==2.11.0 # via sphinx certifi==2022.12.7 # via requests charset-normalizer==2.1.1 # via requests docutils==0.19 # via sphinx idna==3.4 # via requests imagesize==1.4.1 # via sphinx jinja2==3.1.2 # via sphinx markupsafe==2.1.1 # via jinja2 packaging==22.0 # via sphinx pygments==2.13.0 # via sphinx pytz==2022.7 # via babel requests==2.28.1 # via sphinx snowballstemmer==2.2.0 # via sphinx sphinx==5.3.0 # via -r docs.in sphinxcontrib-applehelp==1.0.2 # via sphinx sphinxcontrib-devhelp==1.0.2 # via sphinx sphinxcontrib-htmlhelp==2.0.0 # via sphinx sphinxcontrib-jsmath==1.0.1 # via sphinx sphinxcontrib-qthelp==1.0.3 # via sphinx sphinxcontrib-serializinghtml==1.1.5 # via sphinx urllib3==1.26.13 # via requests
Check list ✅
If you followed this guide, you have pinned:
tool versions (Python, Node)
top-level dependencies (Sphinx, Sphinx extensions)
transitive dependencies (Pytz, Jinja2)
This will protect your builds from failures because of a random tool or dependency update.
You do still need to upgrade your dependencies from time to time, but you should do that on your own schedule.
See also
- Configuration file reference
Configuration file reference
- Build process overview
Build process information
- Build process customization
Customizing builds to do more