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 build
  • check runs Biome lint and formatting diagnostics.
  • test runs the Vitest suite once.
  • build validates 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:crypto and Buffer).
  • observability.enabled: true — enables Worker invocation observability.

One-time Cloudflare setup

Authenticate Wrangler

pnpm wrangler login

For 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-uploads

Copy 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:migrate

This 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_IDX

Set VITE_CREEM_PRODUCT_* and VITE_IS_DEMO in the CI/build environment, not with wrangler secret put.

Deploy

pnpm deploy

Record the deployed version and URL, then run the smoke tests below against that URL—not against localhost.

The four application configuration planes

PlaneProduction sourceExamples
Bindingswrangler.jsoncD1 DB, R2 UPLOADS
Non-secret runtime varswrangler.jsoncvarsBETTER_AUTH_URL
Server secretswrangler secret putAuth/OAuth, Resend, OpenAI, Replicate, Creem
Build-time valuesCI/build environmentVITE_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_URL and BETTER_AUTH_SECRET are 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; /zh pages 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.

On this page