Written by Claude Code — @lucasrodes at the wheel.
The CF Pages parallel mirror is live at https://docs-cf.owid.io/ via three Pages projects (owid-docs, owid-etl-docs, owid-grapher-py-docs) with the umbrella _worker.js proxying subproject paths. RtD remains the authoritative docs.owid.io/ deploy until we cut over. See owid/owid-docs/INFRASTRUCTURE.md for how the pieces fit.
This issue tracks the cut-over plan once we're happy with the CF mirror.
1. Evaluation period (now)
Spot-check these pages on docs-cf.owid.io and confirm parity with RtD. The dynamically generated content is the highest-risk surface — those pages depend on pre-build scripts (docs/ignore/pre-build/*.py) and post-build notebook conversion, which CF runs through the same Make targets as RtD but in a fresh CI env.
- Umbrella —
/ (owid-docs landing, nav into subprojects)
- ETL — dynamically generated (most likely to drift):
/projects/etl/architecture/metadata/reference/ (from bake_metadata_reference.py)
/projects/etl/api/catalog-api/ (from bake_catalog_api.py + lib/catalog README)
/projects/etl/api/search-api/, chart-api/, semantic-search-api/
/projects/etl/guides/analytics-events/ (TypeScript-sourced, fetched from owid-grapher at build time)
/projects/etl/guides/etl-cli/ (CLI help)
- any page under
/projects/etl/analyses/ (Jupyter notebooks → HTML)
- ETL — static:
/projects/etl/, /projects/etl/getting-started/, /projects/etl/contributing/
- owid-grapher-py:
/projects/owid-grapher-py/, /projects/owid-grapher-py/api-reference/grapher/
- Cross-links between subprojects (the umbrella nav and any inline links)
- Search — top-bar search on each subproject (built by Zensical, index regenerated each build)
- Legacy URL redirects —
/en/latest/, /projects/etl/en/latest/..., /projects/owid-grapher-py/en/latest/... should 301 to the canonical paths (see _worker.js in owid-docs)
- Sitemap + robots.txt at each project root
- 404 behavior — visit a nonexistent path under each subproject
Useful comparison: open the same URL on RtD and CF side-by-side. Differences in TOC, headings, or generated content are the signal that something needs fixing before cut-over.
2. Cut-over steps
When sign-off is ready:
3. Cleanup (after cut-over is stable for ~1 week)
Coordination
The DNS swap is the only step that requires the site team (Bastian / Mojmir). Everything else is self-contained in these three docs repos.
The CF Pages parallel mirror is live at https://docs-cf.owid.io/ via three Pages projects (
owid-docs,owid-etl-docs,owid-grapher-py-docs) with the umbrella_worker.jsproxying subproject paths. RtD remains the authoritativedocs.owid.io/deploy until we cut over. Seeowid/owid-docs/INFRASTRUCTURE.mdfor how the pieces fit.This issue tracks the cut-over plan once we're happy with the CF mirror.
1. Evaluation period (now)
Spot-check these pages on
docs-cf.owid.ioand confirm parity with RtD. The dynamically generated content is the highest-risk surface — those pages depend on pre-build scripts (docs/ignore/pre-build/*.py) and post-build notebook conversion, which CF runs through the same Make targets as RtD but in a fresh CI env./(owid-docs landing, nav into subprojects)/projects/etl/architecture/metadata/reference/(frombake_metadata_reference.py)/projects/etl/api/catalog-api/(frombake_catalog_api.py+ lib/catalog README)/projects/etl/api/search-api/,chart-api/,semantic-search-api//projects/etl/guides/analytics-events/(TypeScript-sourced, fetched from owid-grapher at build time)/projects/etl/guides/etl-cli/(CLI help)/projects/etl/analyses/(Jupyter notebooks → HTML)/projects/etl/,/projects/etl/getting-started/,/projects/etl/contributing//projects/owid-grapher-py/,/projects/owid-grapher-py/api-reference/grapher//en/latest/,/projects/etl/en/latest/...,/projects/owid-grapher-py/en/latest/...should 301 to the canonical paths (see_worker.jsin owid-docs)Useful comparison: open the same URL on RtD and CF side-by-side. Differences in TOC, headings, or generated content are the signal that something needs fixing before cut-over.
2. Cut-over steps
When sign-off is ready:
docs.owid.iofrom RtD's CNAME to the CF Pages domain. CF auto-issues a cert.docs.owid.ioto theowid-docsproject (CF dashboard → owid-docs → Custom domains).docs.owid.iofrom the RtDowid-docsproject (RtD admin → Domains) — new finding: the legacy COVID docs (RtD subprojectowidcovid-19-data, never migrated to CF) have their native URLowidcovid-19-data.readthedocs.io302-ing back todocs.owid.io/projects/covid/. If the domain stays configured on RtD after the swap, the COVID docs become unreachable from every URL (native → docs.owid.io → CF 404/redirect loop). 🔨 Cut-over prose + COVID docs redirect for docs.owid.io owid-docs#6 adds a_worker.js301 for/projects/covid/*→ the native RtD URL, which only works once the domain is detached.site_urloverride in each subproject workflow — change the sed line fromhttps://docs-cf.owid.io/...tohttps://docs.owid.io/.... Affects:owid/etl.github/workflows/deploy-docs-cf.yml,owid/owid-grapher-pysame file. → PRs ready: 📜 Point docs site_url at docs.owid.io for the CF cut-over #6217, 🔨 Point docs site_url at docs.owid.io for the CF cut-over owid-grapher-py#28 (safe to merge pre-swap).zensical.tomlalready hassite_url = \"https://docs.owid.io/\"— no change needed._worker.jsuses relative paths only — no change needed.docs-cf.owid.iocustom domain — keep as a staging alias (decided 2026-06-07).docs-cf.owid.ioanddocs.owid.ioacross all three repos, update anything that hardcodes the staging URL. → Done in 📜 Point docs site_url at docs.owid.io for the CF cut-over #6217 (also fixes several already-404 doc links) and 🔨 Cut-over prose + COVID docs redirect for docs.owid.io owid-docs#6 (post-cut-over prose). (The Slack quick-links column we added to owidbot points at*.owid-etl-docs.pages.dev/projects/etl/— that's a branch-alias URL, not adocs.owid.ioURL, so no change.)3. Cleanup (after cut-over is stable for ~1 week)
.readthedocs.yaml(this repo) and.readthedocs.yml(owid-docs, owid-grapher-py).owid-etl,owid-docs,owid-grapher-py).owid-docs/.github/workflows/docs.yml./en/latest/301 rule from_worker.jsonce external links have had time to update (or keep indefinitely — it's tiny and harmless).Coordination
The DNS swap is the only step that requires the site team (Bastian / Mojmir). Everything else is self-contained in these three docs repos.