Skip to content
On this page

Deploy to Cloudflare

Configure resources, build/runtime domains and ordered Worker releases.

The application Worker serves both ToB and ToC and owns D1, cron and inbound processing. The optional email agent transports allowlisted Cloudflare mail. The documentation is a separate static Pages site. Two web hostnames are not two application Workers.

Resources and bindings

Use the current wrangler.jsonc and workers/email-agent/wrangler.jsonc templates in your own account. Replace example domains, zero D1 ID and placeholder product mapping. The example Vectorize index is 1024-dimensional cosine; choose a matching embedding model/index before enabling knowledge.

Resource / bindingResponsibility
D1 DBApplication business data and all migrations
R2 R2Inline images and original knowledge documents
Vectorize VECTORIZEProduct/model knowledge coordinate spaces
R2 EMAIL_STORAGEShared private raw MIME and receipts
Inbound Queue / DLQMain consumes; ingress produces
Outbound Queue / DLQMain produces; optional agent consumes
SEND_EMAILCloudflare Email Sending
WORKER_SELF_REFERENCEApplication background work
Agent MAIN_MAILRestricted main MailAgentGateway RPC

Both queues/bucket bindings are in the main template even when you initially use web tickets only. Each queue has one consumer and a DLQ. Set email R2 lifecycle to 30 days and queue/DLQ retention to 14 days. The agent has no D1 or AUTH_SECRET.

Domains and secrets

Attach distinct admin/customer Custom Domains. Set exact HTTPS BETTER_AUTH_URL, comma-separated ADMIN_DOMAINS/TOC_DOMAINS, and matching .env.production.local from .env.production.example. Build-time middleware values must agree with runtime Wrangler variables; remove conflicting .env.local values. Keep workers_dev disabled and development port routing/debug disabled in production.

Store a generated AUTH_SECRET with Wrangler. It seals saved credentials as well as authenticating requests; preserve a recovery copy and plan rotation on an existing database. Pair build-time NEXT_PUBLIC_TURNSTILE_SITE_KEY with runtime TURNSTILE_SECRET. Both absent disables CAPTCHA intentionally; only the server secret present makes protected customer actions fail closed.

Validate, migrate and release

From the repository root:

Shell
pnpm install --frozen-lockfilepnpm cf-typegenpnpm cf-typegen --checkpnpm lintpnpm testpnpm build:workerpnpm exec drizzle-kit checkpnpm exec wrangler deploy --dry-run

Back up an existing D1, confirm the target account/database, apply all migration files, then deploy the built main Worker:

Shell
pnpm db:migrate:remotepnpm exec wrangler deploy

Do not stop at historical migration 0028 or 0033: the current checkout also contains decision-filter migrations 0034 and 0035. Protect the admin hostname with Access before /admin/install, create the first administrator, then configure product, team, agents, types and forms. Customer identity is required before portal access. pnpm deploy rebuilds/deploys main, not the agent.

Optional email agent order

First deploy main with its RPC export and applied schema. Configure the product's Cloudflare inbound/outbound addresses, EMAIL_AGENT_ADDRESSES in both configs, agent SEND_EMAIL.allowed_sender_addresses, and main EMAIL_AGENT_PRODUCTS with real product IDs. Rebuild/redeploy main after changing bindings/variables, then pnpm deploy:email-agent. Point each allowed recipient's Email Routing rule at one ingress Worker. Sender-domain Sending/Routing verification and DNS are separate operator steps; a support subdomain can use Cloudflare MX while apex MX stays elsewhere.

Static documentation site

apps/site is an independent workspace using svedocs 0.3.0 and SvelteKit 3. Its canonical origin is https://onfire.pwp.sh. Pages settings are project root apps/site, build pnpm install --frozen-lockfile && pnpm build, output build, and SITE_URL=https://onfire.pwp.sh. English is / and /docs/; Chinese is /zh/ and /docs/zh/.... Local search, Markdown twins, llms.txt and sitemap are static; site Ask AI is disabled. Pages publication does not deploy application Workers.

Use private ignored deployment configs for an existing instance; keep public examples and secrets separate. Check operations and MCP Access exceptions before opening a live instance.