Choosing a Versioning Strategy for Read the Docs: Branches vs Tags in Practice
Read the Docs versions map to Git refs, not semantic versions. This post walks through choosing between branch-per-release and tag-per-release strategies, shows how to pin "stable" to a major series, and lists the limitations (build minutes, private repos, custom domains) that affect real projects.
20 Jan 2026, 16:55 UTC

The versioning trap
You push a hotfix to main and suddenly your "stable" documentation shows unreleased features. Or you tag v2.1.0 but the version flyout still only shows "latest" and "stable." Read the Docs makes versioned hosting look automatic, but the platform's defaults assume a specific workflow that many teams don't follow.
The useful takeaway: Read the Docs versions map to Git refs, not semantic versions. Understanding this mapping lets you choose a branch/tag strategy that matches your release process instead of fighting the platform.
How the flyout actually works
When you connect a repository, Read the Docs creates a "version" for each Git branch and tag it discovers. The web UI presents these in a flyout menu labeled "stable," "latest," and any other refs. By default:
- latest tracks the default branch (usually
mainormaster) - stable tracks the most recent semver-looking tag (e.g.,
v1.2.3) - Other tags and branches appear under their own names
This means "stable" is a moving pointer, not a frozen snapshot. If you tag v2.0.0 today, "stable" switches to it immediately. Users bookmarking /en/stable/ see the new version without warning.
Branch-per-release vs tag-per-release: a concrete decision
Consider a team shipping a CLI tool with monthly minor releases and occasional hotfixes. Two common strategies:
Strategy A: Long-lived release branches
main (development)
release/1.x (stable for 1.x series)
release/2.x (stable for 2.x series)
Configure Read the Docs to build release/1.x and release/2.x as explicit versions. The flyout shows "1.x" and "2.x" as selectable options. Hotfixes merged to release/1.x update that version's docs automatically.
Trade-off: You maintain multiple branches. Cherry-picking fixes across release/1.x and release/2.x adds overhead. But readers always see docs matching their installed version.
Strategy B: Tags only, no release branches
main (development)
v1.0.0, v1.1.0, v2.0.0 (tags)
Each release is a tag. Read the Docs builds each tag as a distinct version. The flyout lists "v1.0.0," "v1.1.0," "v2.0.0." "Stable" points to the highest semver tag.
Trade-off: No branch maintenance. But a hotfix requires tagging v1.1.1 and rebuilding. Readers on "stable" jump to the hotfix immediately — good for patches, surprising if the hotfix changes behavior.
Worked example: Pinning "stable" to a major version
Suppose you want "stable" to stay on the 1.x series while 2.x develops on main. Read the Docs lets you set a "Default Version" in the project admin dashboard (Admin → Versions). But that only changes the landing page; the flyout still shows "stable" pointing to the latest semver tag.
Practical workaround: Use a dedicated branch for the public stable series.
- Create branch
stable-1.xfromv1.5.0. - In Read the Docs project settings, activate the
stable-1.xversion. - Set "Default Version" to
stable-1.x. - Optionally, rename the version display name to "1.x (stable)" via the version's edit button.
Now the flyout shows "1.x (stable)" and "latest" (tracking main). Tag v2.0.0 when ready; "stable" in the flyout still points to v2.0.0, but your default landing page stays on the 1.x branch until you switch it.
Verify: Visit https://your-project.readthedocs.io/en/1.x-stable/ and confirm the version selector shows the intended label. Check /en/latest/ reflects main.
Limitations that bite
- Build minutes: Each version builds on every push to its ref. Ten active branches = ten builds per commit. Private projects on paid tiers consume minutes fast.
- Private repos: Free tier only hosts public repositories. Private projects require a Business plan ($50/month at time of writing).
- Custom domains: Only one custom domain per project. If you need
docs.v1.example.comanddocs.v2.example.com, you need separate Read the Docs projects (and separate builds). - Search index: The built-in search indexes all versions together. Users searching "deprecated" may get results from old versions. No per-version search scoping without external tooling.
Check your setup in five minutes
- Push a lightweight tag:
git tag v0.0.1-test && git push origin v0.0.1-test. - In Read the Docs, trigger a manual build for that tag (Versions → Build).
- Open the flyout menu. Confirm the new version appears and the URL pattern matches
/en/v0.0.1-test/. - Check "stable" — did it move to your test tag? If yes, your semver parsing is working.
- Delete the test tag locally and remotely:
git tag -d v0.0.1-test && git push origin :refs/tags/v0.0.1-test. Rebuild; the version disappears from the flyout.
This round-trip validates that your versioning strategy behaves as expected before you ship real releases.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.