How to Build Read the Docs with uv
If your docs already build on Read the Docs, you can now switch .readthedocs.yaml to native uv support instead of maintaining a hand-rolled build.jobs override. Read the Docs added this support on April 21, 2026. The two supported paths are uv sync with a docs dependency group, and uv pip install with a requirements file.
Define a docs dependency group
If your project already has a pyproject.toml, declare a dependency group for the documentation toolchain. Groups are the PEP 735 standard place to keep development-only dependencies out of what your users install:
[dependency-groups]
docs = [
"sphinx>=8.1",
"furo>=2024.8",
"myst-parser>=4.0",
]Pin the versions you actually build with. Dependency groups never ship to PyPI, so they exist only for local and CI installs (see the dependency-groups explainer for the full model).
Wire up .readthedocs.yaml to use uv sync
Create .readthedocs.yaml at the repo root with method: uv and command: sync:
version: 2
build:
os: ubuntu-24.04
tools:
python: "3.13"
python:
install:
- method: uv
command: sync
groups:
- docsRead the Docs runs uv sync against your pyproject.toml and pulls in the docs group alongside the project’s main dependencies. If a uv.lock file is present in the repo, uv installs from it; otherwise it resolves fresh.
The groups key takes either a list of group names or the string all. Use all to install every declared group:
python:
install:
- method: uv
command: sync
groups: allPin Python with build.tools.python
Set build.tools.python to an explicit version that satisfies your project’s requires-python, such as "3.13" or "3.14". Avoid "3" unless you’re willing to let doc builds float to the latest 3.x available on Read the Docs.
Commit uv.lock for reproducible doc builds
A single uv.lock covers both the application or library and the docs build. Commit it to version control and Read the Docs installs the exact resolved versions on every build.
$ git add uv.lock
$ git commit -m "Lock dependencies"
Without uv.lock, every build re-resolves and may pull in newer compatible versions of Sphinx, your theme, or any plugin. That works, but a docs build that succeeded yesterday can fail today on a transitive upgrade. See How to use a uv lockfile for reproducible Python environments for the full mechanics of uv lock and uv sync --locked.
Install from a requirements file instead
Projects that aren’t on pyproject.toml yet can still use uv on Read the Docs by switching command to pip:
version: 2
build:
os: ubuntu-24.04
tools:
python: "3.13"
python:
install:
- method: uv
command: pip
requirements: docs/requirements.txtRead the Docs runs uv pip install -r docs/requirements.txt. Use path: instead of requirements: to install the package itself with optional extras:
python:
install:
- method: uv
command: pip
path: .
extras:
- docsThat maps to uv pip install .[docs].
Avoid the two uv-mode pitfalls
Two rules trip people up on the first migration:
- Only one
python.installentry is allowed whenmethod: uv. The legacy pip method lets you stack a requirements install and a package install. The uv method does not. Fold everything into a singlesync(with groups or extras) or a singlepipinvocation. syncandpipaccept different keys.groupsbelongs tocommand: syncand is rejected undercommand: pip.requirementsbelongs tocommand: pipand is rejected undercommand: sync. Pick the command that matches the input you have (apyproject.tomlforsync, arequirements.txtforpip) and use only that command’s keys.
Reach for build.jobs when uv-mode is too narrow
Native method: uv covers the common cases but does not surface every uv flag. Workflows that need --locked, --frozen, --no-dev, or a multi-step install have to drop down to build.jobs and call uv directly. See Read the Docs build customization for the escape hatch and stay on python.install for everything else.