Publish your site on GitHub Pages
GitHub Pages publishes a static site from your repository’s own workflow, on your domain, at no cost. OpsCrew sets it up: the Pages site, its custom domain, the DNS records on Cloudflare and the workflow, each change after you approve its plan by its hash.
GitHub Pages has more limits than Cloudflare’s hosting: no security headers, no server-side rendering, no runtime secrets and no commercial use. It suits open-source projects and documentation.
When to choose it
Section titled “When to choose it”- The site is static: Astro, Hugo, a Vite or React single-page app, a Next.js static export,
Nuxt’s
nuxt generateor SvelteKit’s static adapter. A server-rendered app needs Cloudflare’s hosting or a server. - The repository is public, on any GitHub plan. A private repository works only on GitHub Pro, Team or Enterprise, and the site is public even then. On GitHub’s free plan, GitHub refuses Pages for a private repository.
- The site is not a business: see GitHub’s terms.
- Your domain’s DNS is on Cloudflare:
npx opscrew domain connect <domain>moves it there (see Buying or connecting a domain).
Design does not offer GitHub Pages, and says why, when the app needs a server (a database, cache, queue, worker, cron job or SQLite of its own), or renders on the server. When the build is given secrets, design warns that they end up in the published site.
For a private repository, OpsCrew reads the owner’s GitHub plan when the GitHub token holds the
optional Plan: Read permission. Without it, design offers GitHub Pages with a caveat, and if GitHub
refuses, npx opscrew provision stops at its first step with nothing changed.
GitHub’s terms and limits
Section titled “GitHub’s terms and limits”As OpsCrew read them on 2026-10-23 from GitHub Pages limits and GitHub’s terms for Pages:
- No commercial use: GitHub’s terms do not allow GitHub Pages for running a business, e-commerce or a commercial SaaS, or for sensitive transactions such as passwords or payments.
- A site of at most 1 GB. The workflow checks the built site first and stops with the limit named.
- A soft limit of 100 GB of traffic a month.
- Deploys time out after 10 minutes.
- No custom response headers. OpsCrew cannot add its security headers (HSTS,
X-Content-Type-Options,X-Frame-Options,Referrer-Policy), and GitHub ignores a_headersor_redirectsfile.npx opscrew securereports this and says what it means. - No server-side rendering and no runtime secrets. Your app’s variables reach only the build, and whatever the build puts into the site is public: anyone can read it.
- No fallback for a single-page app: a path that is no file gets the site’s
404.html, with the status 404. - No previews. One site per repository, so an app on GitHub Pages has prod only.
DNS only, and why
Section titled “DNS only, and why”The site’s DNS records on Cloudflare are DNS-only (the grey cloud):
- the apex’s A and AAAA records to GitHub Pages’ addresses, or a subdomain’s CNAME to
<owner>.github.io; - with
www(only at the apex), a CNAME forwww; GitHub redirects between the two itself.
Cloudflare’s proxy would stop GitHub from issuing and renewing the site’s certificate, so OpsCrew refuses it for these hostnames. That also means Cloudflare’s WAF, and OpsCrew’s probe block, never see the site’s traffic.
A CAA record that lets only other certificate authorities issue certificates for the domain would
keep GitHub from Let’s Encrypt. provision stops before any change and names it.
The tokens
Section titled “The tokens”- GitHub: your
GITHUB_TOKENalso needs Pages: Read and write and Administration: Read and write (GitHub accepts nothing narrower for changing a Pages site), and optionally Plan: Read. See The GitHub token. - Cloudflare: your
CLOUDFLARE_API_TOKENwith the zone permissions in The Cloudflare token, for the DNS records.
Step by step
Section titled “Step by step”npx opscrew interview: choose GitHub Pages (no server). In scripts, answerprovider: github-pageswith yourdomainanddnsProvider: cloudflare.npx opscrew designshows the option, free, with the limits above.npx opscrew provisionchecks the tokens, the repository’s plan, the hostnames and CAA records, and asks for the value of GitHub’s verification record (see Verify your domain). Then, in this order, each after its own approval:- the Pages plan: GitHub Pages turned on, published by the workflow, with your custom domain. GitHub Pages already turned on by hand (from a branch) shows as a change to the workflow source;
- the infrastructure plan for the site’s DNS records, and one for GitHub’s verification record;
- the app’s values for its build, stored in GitHub, by name;
- the pull request with the workflow. Merging it publishes the site.
- Click Verify in your GitHub settings (below).
npx opscrew verifychecks each hostname’s DNS, certificate and answer, and GitHub’s HTTPS, and writesopscrew/RUNBOOK.md.
In scripts, npx opscrew provision --plan-only --pages-verification <value> --yes prints the
plans’ hashes (without the value, a run with no terminal stops before any change and says where to
find it), and npx opscrew provision --pages-verification <value> --approve <hash> … --yes
applies exactly those plans.
The takeover-safe order
Section titled “The takeover-safe order”A DNS record that points at GitHub while no Pages site claims the domain would let anyone’s GitHub Pages site take the domain over. So OpsCrew always sets the custom domain at GitHub before its DNS records exist, and on removal deletes the records first, while the site still claims the domain.
Verify your domain
Section titled “Verify your domain”GitHub’s domain verification makes the domain usable only by your account’s Pages sites, so no one else can claim it. It has one step GitHub offers no API for, so it is yours:
- Open https://github.com/settings/pages (for a repository an organization owns,
https://github.com/organizations/<owner>/settings/pages), click Add a domain, enter your domain and copy the TXT record’s value GitHub shows. Leave Verify for later. - Give the value to
npx opscrew provisionwhen it asks (--pages-verification <value>in scripts). OpsCrew creates the record_github-pages-challenge-<owner>.<domain>on Cloudflare. - Once the record resolves, usually within minutes, click Verify next to the domain on that
page.
provisionprints this step with the exact page.
GitHub serves the site over HTTP at once, and issues its certificate after it has checked the DNS
records, which GitHub says can take up to 24 hours. GitHub’s DNS check took about an hour in
OpsCrew’s tests, and the certificate came only once OpsCrew saved the domain again (below). Until
then, npx opscrew verify and npx opscrew status say that the certificate is pending and when to
look again, and the workflow’s own check is only a warning.
Run npx opscrew provision again once GitHub has checked the DNS records: GitHub requests the
certificate only when the domain is saved with valid records, so OpsCrew saves it again (removing it
and setting it straight back, about a second apart; a verified domain cannot be taken in that
second). Once the certificate is issued, the next provision enforces HTTPS through a Pages plan,
and GitHub redirects http:// to https://. verify never changes anything itself.
How a deploy works
Section titled “How a deploy works”- You push to the deploy branch (a push deploys when it changes a path the site’s build reads: When a push deploys your app), or run the workflow by hand.
- GitHub Actions builds the site, checks it against the 1 GB, and hands it to GitHub Pages with GitHub’s own Pages actions, pinned by their commits.
- The deploy job publishes it in the
github-pagesenvironment, then checks that the site answers.
Rolling back
Section titled “Rolling back”npx opscrew status # the last Pages run, the one before, and the rollback commandnpx opscrew rollback # re-runs the run before the one that published the sitenpx opscrew rollback --to <run id or commit> # or an earlier oneopscrew rollback says first that GitHub builds the site again from that run’s commit (a
dependency may have changed since), shows the run with the approval’s hash, and re-runs it only
after your approval. GitHub then publishes it. GitHub re-runs only runs from the last 30 days; for
an older version, revert the commit and push. In scripts, add --approve <hash>; --yes never
approves a rollback.
Without --to, it re-runs the run that published the site before the one that publishes it now,
when that run is older; otherwise the newest run created before it that published. So a second
rollback goes further back, and never to the run you rolled back from. A run pushed while deploys
were paused published nothing, and never counts. npx opscrew rollback --list shows the
successful runs. It refuses while a deploy run is queued or running, and when the site’s workflow
is not the one its plan generates (run npx opscrew provision and merge its pull request).
Afterwards the site’s deploys pause: a push builds but does not publish, until
npx opscrew rollback --resume. A re-run still publishes, also one you start yourself in GitHub
(Re-run all jobs). See Rolling back a release.
npx opscrew verifychecks the site and writes the runbook.npx opscrew statusshows the Pages site, its certificate, HTTPS and the last runs.npx opscrew driftcompares the Pages site, its DNS records and the verification record with what OpsCrew set up. It changes nothing;npx opscrew provisionputs them back.npx opscrew securereports the missing headers.
Removing the site
Section titled “Removing the site”npx opscrew destroy # asks you to type the app's namenpx opscrew destroy --delete-verification # also deletes GitHub's verification recorddestroy lists what it changes and asks you to type the app’s name (--confirm <app> in scripts).
Then, in the takeover-safe order, each step after its own approval:
- the site’s DNS records are deleted, while the Pages site still claims the domain;
- the custom domain is removed, GitHub Pages is turned off and its
github-pagesenvironment is deleted; - the app’s values for its build are removed from GitHub.
GitHub’s verification record stays, so the domain stays verified for your account. Add
--delete-verification to delete it too, in the same run or later on its own. The files in your
repository stay.
From Claude Code
Section titled “From Claude Code”The OpsCrew plugin offers GitHub Pages in the interview, and every plan and rollback waits for your approval in a dialog. The Verify click stays yours.