Cloudflare Pages (homelab docs)¶
How homelab documentation is built and published to Cloudflare Pages.
Live site: hldocs-c0acdec9.pages.dev
Overview¶
| Item | Value |
|---|---|
| Source | docs/ + mkdocs.yml in homelab repo |
| Build | mkdocs-material, mkdocs build --strict |
| CI | .github/workflows/docs.yml |
| Runner | Selected by repository variable CI_RUNS_ON; see central runner management |
| Python | actions/setup-python@v7, Python 3.13 |
| Node tooling | actions/setup-node@v7, Node 22, npm ci for markdownlint |
| Action runtime | FORCE_JAVASCRIPT_ACTIONS_TO_NODE24=true while the repo completes the Node 24 transition |
| Deploy action | cloudflare/wrangler-action@v4 |
| Pages project | hldocs-c0acdec9 |
| Trigger | Push to main for docs, documentation checks, site config, or their dependency locks; also workflow_dispatch |
| Secrets | GitHub CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID |
The GitHub repository is private; the Pages site is public. Links to repository
manuals require GitHub access. Those manuals are not automatically copied to the
public site. Review the exact contents before moving any additional source into
docs/.
Docs CI is separate from Traefik’s Cloudflare DNS token (services/traefik/.env.sops.yaml).
Use a Pages-scoped API token for GitHub Actions; keep Traefik DNS-01 on its own token
(see Public edge DR).
The Pages site is intentionally public as of 2026-06-26. Do not publish
plaintext secrets, private host keys, private IP credentials, or unredacted
incident artifacts. Contractor assessments and review reports may be published
under docs/reviews/ when they contain links/evidence but no sensitive
material.
Local preview¶
From repo root:
uv sync --locked
npm ci
uv run mkdocs serve # http://127.0.0.1:8000
uv run python scripts/check-docs.py
uv run mkdocs build --strict # same check as CI — must pass before merge
uv run python scripts/check-docs.py --site-dir site
uv run python scripts/lint-markdown.py
Markdown is linted in CI via npm ci + scripts/lint-markdown.py. Use
uv run python scripts/lint-markdown.py --fix before committing docs changes.
Publish pipeline¶
- Merge a reviewed PR to
main. The shared checkout andmainare production inputs. - Deploy Docs workflow runs:
actions/checkout@v7(pinned SHA in workflow)actions/setup-python@v7with Python3.13actions/setup-node@v7with Node22npm cipython scripts/lint-markdown.pypip install uvuv sync --locked(the sameuv.lockused locally; no separate CI-only Material pin)uv run python scripts/check-docs.pyuv run mkdocs build --strictuv run python scripts/check-docs.py --site-dir site- Deploy with
cloudflare/wrangler-action@v4:pages deploy site/ --project-name=hldocs-c0acdec9 --commit-dirty=true - Confirm green run: Actions → Deploy Docs.
- Run
uv run python scripts/check-docs.py --site-dir site --live-url https://hldocs-c0acdec9.pages.devfrom the exact built revision. This compares every expected route with the live sitemap and the searchable page content. A local build passing does not establish that production was updated.
--strict fails on warnings. mkdocs.yml promotes missing navigation entries,
broken heading anchors, and unrecognized relative links to warnings, so these
are also merge blockers. Link to repo files with GitHub blob
URLs, not ../../services/... or ../../scripts/... paths.
The workflow intentionally separates Node concepts:
setup-nodeinstalls Node22for repo-local npm tooling (markdownlint-cli2currently requires Node>=22).FORCE_JAVASCRIPT_ACTIONS_TO_NODE24=trueexercises GitHub JavaScript actions on Node 24 during the platform transition.- Pages project
hldocs-c0acdec9is assumed to already exist; the workflow only deploys. Create/list steps are not part of CI anymore.
Adding pages to navigation¶
Edit mkdocs.yml nav:. Every Markdown page under docs/ must appear exactly once;
the source checker rejects omitted pages, duplicate routes, and duplicate labels within a group.
The strict build also rejects missing targets and broken anchors.
Choose the section by what the reader needs, not by the source directory:
| Section | Contents |
|---|---|
| Home | Task shortcuts and the repository project map |
| Runbooks | Operating procedures grouped by task |
| Infrastructure | Network, compute, platform design, hosts, and appliances |
| Services | Service guides, operator manuals, and customer apps |
| Security | Risk register and operator handover |
| History | Journals, dated scans, reviews, postmortems, and retired systems |
- Add a runbook under the appropriate task group and to the runbook directory.
- Add journals under History → Journal → month, newest first, and to the
journal index. Keep
journal/index.mdfirst in its navigation group. - Put reviews under History → Reviews and audits. Keep a package README with its executive summary and links.
- Move retired pages into the relevant History group, keeping the original file path and URL.
- Include capture dates in scan labels. Do not label a historical snapshot simply “live.”
- Put section index pages first. Keep
navigation.tabsandnavigation.indexesenabled; do not restorenavigation.expand, which opens the entire tree by default.
Do not publish scratch files or duplicate copies to make an omitted-page check pass. Resolve their ownership before adding, moving, or deleting them. See History for the archive policy.
Optional hardening¶
| Task | Status | Notes |
|---|---|---|
| Pages deploy via GitHub Actions | ✅ Done | Secrets in repo settings |
| Cloudflare Access on docs URL | ❌ Declined | Owner accepted public Pages site (2026-06-26) |
Troubleshooting¶
Deploy Docs failed on mkdocs build --strict¶
- Read the log for
WARNINGlines (strict treats them as errors). - Common fixes:
- Replace
../../services/.../../../scripts/...links withhttps://github.com/spadoople/homelab/blob/main/... - Fix stale
#anchorlinks after heading renames. - Reproduce locally:
uv run mkdocs build --strict.
Node or Wrangler errors¶
- Confirm the job is using
actions/setup-node@v7with Node22; that controls npm tooling, not the JavaScript action runtime. - Keep
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24=trueunless the Node 24 transition has been retired across all workflows. - Wrangler deploy failures are usually token scope or project name mismatches —
confirm
CLOUDFLARE_API_TOKENcan edit Pages and the project ishldocs-c0acdec9.
Pages project missing¶
The workflow does not auto-create the project. If deploy fails because the
project is absent, create hldocs-c0acdec9 once in the Cloudflare dashboard
(or via Wrangler CLI) with production branch main, then re-run Deploy Docs.
Token needs Account → Cloudflare Pages → Edit.
Site stale after green deploy¶
Hard-refresh or check deployment in Cloudflare dashboard → Workers & Pages →
hldocs-c0acdec9 → Deployments.
Related¶
- Documentation validation — audit + publish checklist
- Project journal
- dr-public-edge.md — Traefik/Authentik incident response (not Pages)