infra/web manages the Cloudflare Worker, immutable Worker versions, the live
deployment, and the custom domain. Two deployments share this configuration:
development on next.sable.moe from pushes to main, and production on
app.sable.moe from v* tags.
Each deployment has its own OpenTofu state.
Prerequisites:
- OpenTofu
1.11.xinstalled locally - Node.js/pnpm installed locally so you can build
dist/before Worker uploads - A Cloudflare account with the
sable.moezone onboarded to Cloudflare - A GitLab project to store the OpenTofu state
- A GitLab access token that can read and write that project's OpenTofu state
Required repository secrets:
TF_CLOUDFLARE_API_TOKENTF_VAR_ACCOUNT_IDTF_VAR_ZONE_IDTF_HTTP_USERNAMETF_HTTP_PASSWORD
Per-deployment secrets. Development uses the plain names, production the same
names with a _PRODUCTION suffix:
TF_HTTP_ADDRESS,TF_HTTP_LOCK_ADDRESS,TF_HTTP_UNLOCK_ADDRESSTF_VAR_CUSTOM_DOMAINTF_VAR_WORKER_NAME
Development is next.sable.moe on the sable-next Worker. Production is
app.sable.moe on the sable Worker. The HTTP addresses of each deployment must
point to its own state. The workflow maps these values to the environment
variable names used by OpenTofu and Cloudflare.
A custom domain can be attached to only one Worker. Before the first production
apply, import the existing app.sable.moe Worker and custom domain into the
production state, and stop the old deployment from applying to it.
Cloudflare API token permissions:
Account > Workers Scripts > Edit- Scope the token to the account that owns the Worker.
- Scope the token to the zone serving
next.sable.moeandapp.sable.moe. - No Pages or DNS edit permission is required; Cloudflare creates the DNS record when the custom domain is attached.
GitLab access token permissions:
api
Local setup:
- Copy
terraform.tfvars.exampletoterraform.tfvarsand fill in the shared Cloudflare account and zone values. - Copy
gitlab.http.tfbackend.exampletogitlab.http.tfbackendand replace the GitLab project ID and username. - Run
pnpm installfrom the repository root. - Export the GitLab token as
TF_HTTP_PASSWORDand the Cloudflare API token asCLOUDFLARE_API_TOKEN. - Build the web assets before planning or applying.
Local OpenTofu flow from the repository root:
pnpm run build
tofu -chdir=infra/web init -input=false -reconfigure -backend-config="../gitlab.http.tfbackend"
tofu -chdir=infra/web validate
tofu -chdir=infra/web plan -var-file="../terraform.tfvars"
tofu -chdir=infra/web apply -var-file="../terraform.tfvars"The workflow is .forgejo/workflows/cloudflare-web.yml. It builds the web
assets once per push or pull request, reuses that artifact for frontend checks,
Playwright, desktop packaging, and Cloudflare operations, plans infrastructure
changes, applies the development state on pushes to main, applies the
production state on v* tags or a manual dispatch with git_tag, and uploads
same-repository PR preview versions using Cloudflare Worker preview aliases.
The Worker is configured with single-page-application not-found handling so
client-side routes continue to resolve to the static SvelteKit shell.