Docs site deploy (Cloudflare Pages + Access)
The docs site at apps/docs/ (Nextra v4) deploys to Cloudflare Pages and is gated behind Cloudflare Access. This runbook covers the one-time setup, the automated deploy pipeline, and how to manage who can read the docs.
What ships where
| Surface | URL | Auth |
|---|---|---|
| Local dev | http://localhost:3030 | none |
| Production | https://fitkit-docs.pages.dev (or custom domain) | Cloudflare Access |
| Source | /docs/ in this repo (canonical markdown) | repo permissions |
Pipeline
.github/workflows/deploy-docs.yml triggers on main pushes that touch docs/**, apps/docs/**, or the workflow itself.
push to main with docs change
↓
build apps/docs (pnpm install + pnpm build → out/)
↓
cloudflare/wrangler-action@v3 → `wrangler pages deploy out --project-name=fitkit-docs --branch=main`
↓
Cloudflare Pages serves out/ from fitkit-docs.pages.dev
↓
Cloudflare Access gates the request (SSO / email policy)Builds run on every matching push. Concurrency group docs-deploy prevents overlapping runs.
One-time setup
1. Create the Cloudflare Pages project
In the Cloudflare dashboard → Workers & Pages → Create → Pages → Direct Upload → name it fitkit-docs. Direct Upload (not Git integration) is correct here — the GitHub workflow does the building, Cloudflare just hosts the artifact.
You can leave the production branch blank; the workflow passes --branch=main explicitly.
2. Mint a Cloudflare API token
Cloudflare dashboard → My Profile → API Tokens → Create Token → Custom token.
Permissions needed:
- Account → Cloudflare Pages → Edit
Scope: limit to the specific account that owns fitkit-docs. Copy the token immediately; you won’t see it again.
3. Find your account id
Cloudflare dashboard → any zone or the Workers & Pages page → right sidebar → Account ID. Copy it.
4. Add the two secrets to GitHub
Repo → Settings → Secrets and variables → Actions → New repository secret:
| Name | Value |
|---|---|
CLOUDFLARE_API_TOKEN | Token from step 2 |
CLOUDFLARE_ACCOUNT_ID | Account id from step 3 |
5. Configure Cloudflare Access
Cloudflare Zero Trust dashboard → Access → Applications → Add an application → Self-hosted.
Settings:
- Application name:
FitKit docs - Application domain:
fitkit-docs.pages.dev(or your custom domain) — path/* - Session duration: whatever fits (24h is reasonable)
Then add a policy:
| Action | Allow |
|---|---|
| Include | Emails ending in @desmotech.com (or specific email list) |
| Optional require | Google / Microsoft / GitHub SSO |
Multiple policies can stack — e.g., a permissive “Allow team emails” and a more restrictive “Require MFA for these paths”. Default is single-policy.
6. (Optional) Custom domain
If you want docs.fitkit.fit instead of the .pages.dev URL:
- Cloudflare Pages project → Custom domains → Set up a custom domain →
docs.fitkit.fit. - Add the CNAME Cloudflare provides at your DNS provider (or directly in Cloudflare if the zone is there).
- Update the Access application domain to match.
- (No workflow change needed —
wrangler pages deploydoesn’t care about the public hostname.)
Day-to-day
Triggering a redeploy
Just push to main with a change under docs/** or apps/docs/**. Or run the workflow manually: Actions → Deploy docs to Cloudflare Pages → Run workflow.
Cloudflare retains the last several deployments — you can roll back from the Pages project dashboard if a deploy goes bad.
Granting access
Cloudflare Zero Trust → Access → Applications → FitKit docs → Policies → edit. Add an email, remove one, or add a whole identity provider group. No deploy needed; Access policies apply immediately.
Revoking access
Same place. Removing the policy locks the user out within seconds.
Adding a new doc
Edit any file under /docs/ (canonical source). Push to main. The deploy workflow runs automatically. Don’t edit apps/docs/content/ — it’s a regenerated mirror.
Why this setup, not GitHub Pages
- Auth. GitHub Pages on a regular plan is public-only. Cloudflare Access lets us gate by SSO / email without a GitHub Enterprise Cloud subscription.
- Custom domain. Pages supports custom domains under the same Access policy; GitHub Pages would require the repo to be public anyway.
- Faster invalidation. Cloudflare’s CDN updates within ~30s of a deploy. GitHub Pages can lag.
Failure modes
| Symptom | Cause | Fix |
|---|---|---|
| Workflow fails at “Deploy to Cloudflare Pages” with auth error | CLOUDFLARE_API_TOKEN missing / expired / wrong scope | Re-mint per step 2; resave secret. |
| Workflow succeeds but URL still shows old content | Cloudflare CDN cache | Cloudflare Pages bypasses cache automatically per deploy. If stuck, Purge Everything on the project’s zone. |
| Pages live but every request hits a Cloudflare Access login loop | Domain not added to Access application, or policy excludes you | Zero Trust → Access → Applications → edit domain / policy. |
Build fails at pnpm install | Lockfile drift after deps added | The workflow runs pnpm install --frozen-lockfile=false so this is rare — check the failing log. |
| Nextra build hangs on Pagefind indexing | Memory cap on the GitHub Actions runner | Drop search: { codeblocks: true } to search: true in apps/docs/next.config.mjs temporarily. |
Related
apps/docs/README.md— local dev recipe- Cloudflare Pages docs: https://developers.cloudflare.com/pages/
- Cloudflare Access: https://developers.cloudflare.com/cloudflare-one/applications/configure-apps/