ADR 006: Docs on Cloudflare Pages, not laptop tunnel¶
Status: accepted Date: 2026-05-04 Supersedes: —
Context¶
Earlier in the session we'd put the MkDocs Material docs site behind
the same Cloudflare Tunnel as the warehouse UI, routed by path
(/docs/* → localhost:8766). It worked, but felt wrong:
- Static content was tied to laptop uptime. Docs are markdown → HTML; there's no reason they should disappear when the Mac sleeps.
- Adding a launchd KeepAlive job for
mkdocs servedoesn't actually fix laptop-dependence — KeepAlive only restarts on crash, not when the Mac is closed. "24/7" is a lie when the underlying machine isn't. - A
cloudflared_watcher.sh+fswatchscript existed only to fix a problem (cloudflared not hot-reloading config.yml) that I'd caused by editing config.yml without restarting cloudflared — band-aid for a one-time mistake, not infrastructure. - ADR 002's principle was "use the least-laptop-dependent tier you can, given the data dependencies." The docs have NO data dependencies — they're pure markdown. Putting them on the laptop was the wrong tier.
Decision¶
Static docs go to Cloudflare Pages, deployed via GitHub Actions on
push to main. Custom domain TBD (currently casey-system-docs.pages.dev;
can move to docs.caseymanos.com via dashboard when needed).
The warehouse UI + cloudflared tunnel stay on the laptop, started
manually via kbui — both have hard dependencies on the laptop
(local DuckDB, exposing localhost) and there's no benefit to
faking always-on for them.
What this implies for each piece¶
| Piece | Tier | Always-on? | Why |
|---|---|---|---|
| Docs site | Cloudflare Pages | yes (edge) | Static, no data dep |
| OTQCheckinAgent | Cloudflare Worker | yes (edge) | Already there per ADR 002 |
| Warehouse UI | Laptop, kbui | only when laptop awake | Hard data dep on local DuckDB |
| Tunnel | Laptop, kbui | only when laptop awake | Only exposes laptop; meaningless without it |
| daily_sync, podcast-sync | Laptop, launchd | scheduled, laptop-bound | Hard data dep, scheduled |
Workflow¶
Editing docs:
cd ~/casey-system-docs- Edit markdown
- (optional) Local preview:
uv run mkdocs serve git commit && git push- GHA workflow
.github/workflows/deploy-docs.ymlbuilds + deploys to Pages. Live in ~30s.
The push-to-main flow is the "deploy" — no separate ritual. If GHA
is slow or fails, manual fallback: npx wrangler pages deploy _site.
Removed (cleanup of the tunnel-subpath approach)¶
~/.cloudflared/config.yml— reverted to single ingress rule (warehouse.caseymanos.com → localhost:8765). No/docs/*path routing anymore.~/Library/LaunchAgents/com.casey.{uvicorn,mkdocs,cloudflared}.plist— deleted. Were trying to fake always-on for laptop-bound things.~/garmin-warehouse/scripts/cloudflared_watcher.sh— deleted. Was only needed because of the path-routing complexity.~/garmin-warehouse/scripts/run_ui.sh— reverted to its original 2-process form (uvicorn + cloudflared). No longer starts mkdocs.
Consequences¶
Good:
- Docs really are 24/7 — Cloudflare Pages has its own SLA, no laptop
- One push deploys; no manual sync needed
- Removes 3 launchd plists + a wrapper script + tunnel config
complexity
- ADR 002 + ADR 006 together state a clean rule: "static and
data-independent = edge tier; data-bound = laptop tier"
- GHA history serves as a deploy log (vs wrangler pages deploy
which leaves no audit trail)
Tradeoffs: - Local mkdocs serve preview doesn't match exactly what Pages serves (minor differences in URL paths, etc.) — small in practice - One more GitHub repo (private) to maintain. Trivial. - If Cloudflare Pages quota changes (currently free for our usage), could need to migrate. Migration would be straightforward since the source is just markdown. - Docs no longer behind Cloudflare Access. Repo is private; site contains internal infrastructure details. To re-add gating: CF dashboard → Pages → casey-system-docs → Custom domains → Access policy. Defer until/unless the site gains content that warrants it.
Alternatives considered¶
- GitHub Pages instead of Cloudflare Pages — works fine but would require a separate workflow and we'd lose the option to easily put it behind CF Access if needed.
- MkDocs build artifact committed to gh-pages branch — works but feels archaic; CF Pages handles the build better.
- Leave docs on the laptop tunnel + add launchd — the path I went down briefly. Wrong tier for the content.
Required follow-up (manual, one-time)¶
To activate auto-deploy on push:
- Create Cloudflare API token with Pages:Edit scope at: https://dash.cloudflare.com/profile/api-tokens
- Add to GitHub repo secrets as
CLOUDFLARE_API_TOKEN:gh secret set CLOUDFLARE_API_TOKEN --repo caseymanos/casey-system-docs - Optional: Add custom domain
docs.caseymanos.comvia dashboard (Workers & Pages → casey-system-docs → Custom domains). - Optional: Add Access policy to gate the public site.
References¶
- Pages project:
casey-system-docs(deployed atcasey-system-docs.pages.dev) - GitHub repo: https://github.com/caseymanos/casey-system-docs (private)
- Workflow:
.github/workflows/deploy-docs.yml - Related: ADR 002 — same principle ("least-laptop-dependent tier")