Deployment
Deploy the TanStack Start app, D1 database, and R2 bucket to Cloudflare Workers.
The template deploys to Cloudflare Workers. It does not target Vercel, Docker, or a long-running Node server: Vite builds a Worker bundle and Wrangler publishes it with D1 and R2 bindings.
Quality gate
Run the template's checks before every production deployment:
pnpm check
pnpm test
pnpm buildcheckruns Biome lint and formatting diagnostics.testruns the Vitest suite once.buildvalidates the Vite/TanStack Start production bundle.
Do not deploy with failing checks or an unreviewed database migration.
Build and deploy pipeline
pnpm deploy is exactly:
vite build && wrangler deploy@cloudflare/vite-plugin regenerates dist/server/wrangler.json during the
build. The generated deployment config points main to index.js and assets to
the built client directory, overriding the source-oriented main and assets
values in the root wrangler.jsonc. Wrangler then deploys that generated
configuration.
The root config also defines:
compatibility_date— pin and deliberately update Worker runtime behavior.compatibility_flags: ["nodejs_compat"]— required by dependencies such as the Creem signature code (node:cryptoandBuffer).observability.enabled: true— enables Worker invocation observability.
One-time Cloudflare setup
Authenticate Wrangler
pnpm wrangler loginFor CI, use a scoped Cloudflare API token instead of interactive login.
Create D1 and R2 resources
pnpm wrangler d1 create soar-tanstack
pnpm wrangler r2 bucket create soar-tanstack-uploadsCopy the returned D1 database_id into wrangler.jsonc. If you choose other
resource names, update the config and the db:migrate package script together.
Review bindings and the Worker name
wrangler.jsonc should point at your resources:
{
"name": "your-worker-name",
"d1_databases": [
{
"binding": "DB",
"database_name": "soar-tanstack",
"database_id": "your-d1-database-id",
"migrations_dir": "drizzle"
}
],
"r2_buckets": [
{ "binding": "UPLOADS", "bucket_name": "soar-tanstack-uploads" }
]
}Apply remote D1 migrations
Back up an existing database, review drizzle/, then run:
pnpm db:migrateThis applies committed migrations to soar-tanstack --remote. It does not use
drizzle-kit push.
Configure production values
Set BETTER_AUTH_URL in wrangler.jsonc vars, then add each enabled service's
secret interactively:
pnpm wrangler secret put BETTER_AUTH_SECRET
pnpm wrangler secret put GITHUB_CLIENT_ID
pnpm wrangler secret put GITHUB_CLIENT_SECRET
pnpm wrangler secret put GOOGLE_CLIENT_ID
pnpm wrangler secret put GOOGLE_CLIENT_SECRET
pnpm wrangler secret put RESEND_API_KEY
pnpm wrangler secret put OPENAI_API_KEY
pnpm wrangler secret put REPLICATE_API_TOKEN
pnpm wrangler secret put CREEM_API_KEY
pnpm wrangler secret put CREEM_WEBHOOK_SECRET
pnpm wrangler secret put CREEM_SERVER_IDXSet VITE_CREEM_PRODUCT_* and VITE_IS_DEMO in the CI/build environment, not
with wrangler secret put.
Deploy
pnpm deployRecord the deployed version and URL, then run the smoke tests below against that URL—not against localhost.
The four application configuration planes
| Plane | Production source | Examples |
|---|---|---|
| Bindings | wrangler.jsonc | D1 DB, R2 UPLOADS |
| Non-secret runtime vars | wrangler.jsonc → vars | BETTER_AUTH_URL |
| Server secrets | wrangler secret put | Auth/OAuth, Resend, OpenAI, Replicate, Creem |
| Build-time values | CI/build environment | VITE_CREEM_PRODUCT_*, VITE_IS_DEMO |
.env and .dev.vars are local files and are never uploaded by deploy.
A value working locally says nothing about whether its production plane is
configured. Also remember that changing VITE_* requires a rebuild.
Remote Drizzle Kit HTTP credentials form a fifth, tooling-only plane; the running Worker does not need them. See Environment Variables.
Custom domain and canonical origin
The template uses a Wrangler custom-domain route:
"routes": [
{ "pattern": "app.example.com", "custom_domain": true }
],
"vars": {
"BETTER_AUTH_URL": "https://app.example.com"
}The domain must be in a Cloudflare zone available to the deploying account.
BETTER_AUTH_URL must exactly match the public origin (scheme, host, and any
nonstandard port). Update OAuth callbacks and the Creem webhook to the same
origin.
Pre-launch checklist
- Worker name, custom domain, site metadata, support addresses, and legal content are yours.
- Production D1 and R2 exist; IDs/names match
wrangler.jsonc. - Remote D1 has every reviewed migration and a tested backup/restore path.
-
BETTER_AUTH_URLandBETTER_AUTH_SECRETare set for the production origin. - GitHub/Google callbacks use
/api/auth/callback/<provider>on that origin. - Resend has a verified domain and valid sender address.
- Creem products match display prices; the production webhook is exactly
/api/payment/notify/creem. - The build was rerun after the last
VITE_CREEM_PRODUCT_*change. - OpenAI and Replicate keys are present only for enabled features.
- Public AI endpoints have authentication, rate limiting, input validation, quotas/cost caps, abuse controls, and monitoring.
- Payment cancellation/refund/failure events and user entitlement rules are implemented.
Production smoke tests
- English pages are unprefixed;
/zhpages render and retain locale through auth. - Register, verification email, login, reset, logout, and protected redirects work.
- A real/test-mode payment completes and the signed webhook updates D1 once.
- Dashboard and authenticated user APIs reject logged-out access.
- Docs, blog, and legal content render.
- Chat and each enabled media generator enforce production access/usage policy.
- Authenticated upload writes to R2 and the Worker serves the object back.
- Worker logs show no binding, secret, compatibility, or uncaught runtime errors.
Operations
- Export D1 on a schedule appropriate for your recovery objectives, and test restores before relying on them.
- Inspect live logs with
pnpm wrangler tail; connect error reporting and alerts in addition to the built-in observability setting. - Monitor Worker errors/latency, D1 queries/storage, R2 usage, email delivery, AI cost, and webhook failures.
- Rotate secrets with
wrangler secret put, validate the new value, then revoke the old provider credential. Maintain an incident-safe rotation runbook. - Deploy schema changes in an order compatible with both the old and new Worker when zero-downtime rollout matters.
