Cloudflare
GuideMode is built for Cloudflare Workers, and this is how we run the hosted product. On your own account, the server runs at the edge with Cloudflare’s own storage, queues and workflow runtime instead of the Postgres-backed equivalents used elsewhere.
To get the deployment configuration and a walkthrough, contact info@guidemode.dev.
What it needs
Section titled “What it needs”| Resource | What it is for |
|---|---|
| Worker | The API and the UI. The built UI ships with the Worker as static assets, so there is no second deployment and no bucket to serve from |
| Hyperdrive | Pooled edge connections to your Postgres. We use Neon; any Postgres 17 reachable from Cloudflare works |
| R2 bucket | Session transcripts |
| KV namespace | Rate limits and operation locks |
| Two queues | Session processing and billing updates, each with a producer and a consumer |
| Eleven Workflows | Provider syncs, AI assessment processing and change scoring |
| A second Worker | The notifications Durable Object, bound cross-script for live progress |
| Cron triggers | Nine schedules driving fact refreshes, token renewal, snapshots and maintenance |
Order of operations
Section titled “Order of operations”The notifications Worker deploys before the main Worker, because the main Worker’s binding to it is cross-script and will not resolve otherwise. Database migrations run before either.
Plain variables and secrets
Section titled “Plain variables and secrets”Settings reach a Worker two ways. Non-sensitive values live in the Worker configuration as plain variables; everything that is a credential is a secret, set once per Worker and never committed.
These are the plain variables:
NODE_ENV, RELEASE_VERSION, APP_URL, GITHUB_APP_NAME, GOOGLE_REDIRECT_URI,
GITLAB_REDIRECT_URI, JIRA_REDIRECT_URI, LINEAR_REDIRECT_URI, PADDLE_ENABLED,
PADDLE_CLIENT_TOKEN, the four Paddle price identifiers, AI_PROVIDER and AI_MODELS.
Everything else is a secret:
# Always requiredwrangler secret put OAUTH_ENCRYPTION_KEYwrangler secret put DATABASE_URL
# GitHub App: repository, issue and pull request syncwrangler secret put GITHUB_APP_IDwrangler secret put GITHUB_APP_PRIVATE_KEYwrangler secret put GITHUB_APP_WEBHOOK_SECRET
# Sign-in providers, for the ones you offerwrangler secret put GITHUB_CLIENT_IDwrangler secret put GITHUB_CLIENT_SECRETwrangler secret put GOOGLE_CLIENT_IDwrangler secret put GOOGLE_CLIENT_SECRETwrangler secret put GITLAB_CLIENT_IDwrangler secret put GITLAB_CLIENT_SECRETwrangler secret put MICROSOFT_CLIENT_IDwrangler secret put MICROSOFT_CLIENT_SECRET
# Work tracking integrations, for the ones you usewrangler secret put JIRA_CLIENT_IDwrangler secret put JIRA_SECRETwrangler secret put LINEAR_CLIENT_IDwrangler secret put LINEAR_CLIENT_SECRETwrangler secret put NOTION_CLIENT_IDwrangler secret put NOTION_CLIENT_SECRETwrangler secret put NOTION_WEBHOOK_SECRETwrangler secret put SLACK_CLIENT_IDwrangler secret put SLACK_CLIENT_SECRETwrangler secret put SLACK_SIGNING_SECRET
# AI analysiswrangler secret put GEMINI_API_KEYwrangler secret put OPENAI_API_KEYwrangler secret put CLAUDE_API_KEYwrangler secret put ANTHROPIC_API_KEY
# Email, and optional extraswrangler secret put RESEND_API_KEYwrangler secret put GITHUB_PUBLIC_TOKENwrangler secret put ADMIN_SECRET
# Billing, only when PADDLE_ENABLED is truewrangler secret put PADDLE_API_KEYwrangler secret put PADDLE_WEBHOOK_SECRET
# Tracing, only when LANGFUSE_ENABLED is truewrangler secret put LANGFUSE_PUBLIC_KEYwrangler secret put LANGFUSE_SECRET_KEYOnly the first two are mandatory. Every other line belongs to a feature, and a feature whose settings are absent is switched off rather than broken. Where a group is listed together it must be set together, or the Worker will refuse to start and name what is missing.
Two groups from the configuration reference do not
apply here. The S3_* group is replaced by the R2 binding, and the JOBS_* settings have
no meaning on a runtime with no long-lived process to poll in.
Secrets are per Worker, so a staging Worker needs its own copy of every one of them.
Deployment credentials
Section titled “Deployment credentials”Separately from the Worker’s own settings, whatever runs your deploys needs a Cloudflare API token, your account identifier, and the database URL for the environment being migrated. Those are credentials for the pipeline, not for the application.
Workflow names are account-scoped
Section titled “Workflow names are account-scoped”A Workflow’s name is unique per Cloudflare account, not per Worker. Whichever script deploys a given name last owns it, and every instance created under that name runs with the owning script’s bindings.
If you run more than one environment on a single account, suffix every Workflow name in the non-production configuration. When names collide the failure is silent and expensive: your production API starts a sync, the run executes against the other environment’s database, and production is left with a sync that never progresses.
wrangler workflows list # name -> owning scriptCPU limits
Section titled “CPU limits”The Worker sets the platform maximum of five minutes of CPU per invocation. This is script-wide rather than per-Workflow, and billing is on CPU actually consumed, so ordinary traffic costs no more for it. Leave it in place: the default of thirty seconds is enough to kill a large repository ingest partway through.
Keep the limit identical across every environment’s configuration. The same class runs in all of them, so a difference means a step that dies in one place and nowhere else.
Releases
Section titled “Releases”Deploys run from CI, not a workstation, and promotion is deliberate:
- Push a version tag. Migrations run against the staging database, then the staging Worker deploys
- Publish a release on that tag. Migrations run against the production database, then the production Worker deploys from the same tag staging validated
The version in the server’s manifest must already match the tag, or the pipeline stops before it touches anything.
Choosing this over a container
Section titled “Choosing this over a container”Cloudflare is the right choice when you want managed scaling and edge latency, and when your data may live in Cloudflare’s network. If it may not, use Docker or Kubernetes, which run the same product against your own Postgres and object store.
