CoreMVP

Deploy to Cloudflare

View as Markdown

Deploy the TanStack Start app as one Cloudflare Worker with hosted Supabase. Start with the TanStack Start app working locally. The Next.js template deploys through Vercel.

Before you deploy

Use your application's Cloudflare account, hosted Supabase project and test-mode billing configuration. Install the Cloudflare CLI and confirm your selected account:

npm i -g cf
cf --version
cf auth whoami

The build output is compatible with cf 1.0.0-beta.14. If you are logged out, run cf auth login, then check the account again. Stop if it is not the account that should own your app. Check Worker size limits: the included app is larger than the 3 MiB compressed free-plan limit. Do not assume that a successful local build fits your plan.

Name the Worker and choose its origin

In , change nitro.cloudflare.wrangler.name from app to your application's Worker name. The Cloudflare configuration is inside the existing nitro(...) call. Keep the pinned compatibilityDate; changing it changes Worker runtime behavior.

Record the intended HTTPS origin, https://<worker-name>.<your-subdomain>.workers.dev. <your-subdomain> is your account's workers.dev subdomain, shown on the Workers & Pages overview in the Cloudflare dashboard. Use this origin for browser configuration, Auth redirects and billing return URLs. A custom domain can be configured later through Cloudflare.

In the Supabase dashboard, create or select the hosted project for your application. Confirm its project name and reference before linking this checkout:

bunx supabase login
bunx supabase link --project-ref <your-project-ref>
bunx supabase migration list --linked
bunx supabase db push --linked --dry-run

The dry run lists pending migrations without applying them. For a new project, review that list, apply the template migrations, then check the migration history again:

bunx supabase db push --linked
bunx supabase migration list --linked

All checked-in migration versions must appear in the remote history. For an existing or shared project, stop on an unexpected migration or schema mismatch and reconcile it before deploying. Do not reset an existing database.

Allow Auth redirects

Under Authentication → URL Configuration, add these exact Redirect URLs, replacing the example origin with your Worker origin:

https://<worker-name>.<your-subdomain>.workers.dev/auth/callback
https://<worker-name>.<your-subdomain>.workers.dev/auth/reset-password
https://<worker-name>.<your-subdomain>.workers.dev/dashboard/account?status=Success!&status_description=Your%20email%20has%20been%20updated.
  • The callback receives signup, email and OAuth returns.
  • The reset URL receives password setup and recovery links.
  • The account URL receives email-change confirmation, including the query string the app generates.

If the Worker is the project's only app, set Site URL to its HTTPS origin. When adding Workers to an existing shared project, preserve its Site URL and all existing redirect entries, and add the Worker URLs alongside them. Keep the configured provider callback in Supabase's OAuth settings unchanged. Test each enabled account flow at the Worker URL before advertising it.

Create the Hyperdrive configuration

Create a Hyperdrive configuration for this database by following Cloudflare's Supabase guide, or reuse an existing configuration that points to that exact database. Hyperdrive is a connection proxy; it does not create another database.

  • Use the Supabase direct connection as its origin.
  • Disable query caching so permissions and application reads use current database state.
  • Keep origin TLS enabled. If the database uses a private CA, configure its trusted CA certificate rather than disabling TLS.

List your configurations and record the ID of the one for this database:

cf hyperdrive list

Set that ID as CLOUDFLARE_HYPERDRIVE_ID in the deployment file below. The build binds it as HYPERDRIVE; the Worker uses its connection string with request-scoped clients and prepared statements.

Optional: register billing webhooks

Hosted billing and production email on Workers are not yet verified. You can deploy without them and add them later. If you configure billing now, stay in Stripe test mode or Polar sandbox and register a new hosted webhook destination for your provider at your Worker origin:

https://<worker-name>.<your-subdomain>.workers.dev/api/webhooks/stripe
https://<worker-name>.<your-subdomain>.workers.dev/api/webhooks/polar

Use the signing secret of that hosted destination, never your local listener's secret.

Prepare deployment settings

Create an ignored deployment file from the example:

cp .env.example .env.cloudflare

Edit locally with your provider values. Replace all loopback URLs and placeholders. Collect the Supabase values from the dashboard first:

  • Under Connect, copy the hosted PostgreSQL connection for DATABASE_URL; Supabase's connection guide helps you choose a connection type. The Worker itself connects through the Hyperdrive binding created above.
  • Under Project Settings → API Keys, copy the publishable and server-only secret keys, and note the project's API URL.

At minimum, configure:

SettingValue for this deployment
NEXT_PUBLIC_APP_URLThe exact HTTPS Worker origin.
NEXT_PUBLIC_SUPABASE_URLYour hosted Supabase API URL.
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEYThe matching public key.
DATABASE_URLYour hosted Postgres connection for database tooling and Node. Server-only.
CLOUDFLARE_HYPERDRIVE_IDExisting Hyperdrive configuration ID for this same database; used at build time.
SUPABASE_SECRET_KEYThe matching server-only Supabase key.
EMAIL_DELIVERY_MODEreal, set before any flow sends email.
PAYMENT_PROVIDER and the provider's keysOptional for a first deploy; see the list below.

Also set, for the same file:

  • Email: RESEND_API_KEY and RESEND_FROM_EMAIL. A production build rejects an unset EMAIL_DELIVERY_MODE when it sends email.
  • Stripe: PAYMENT_PROVIDER=stripe with STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY and NEXT_PUBLIC_STRIPE_MODE.
  • Polar: PAYMENT_PROVIDER=polar with the POLAR_* settings in .

leaves SUPABASE_SECRET_KEY commented out; uncomment it. The deploy step uploads every setting in this file as a Worker secret, so replace each replace_me placeholder you keep. Before you sell, also set the product and price IDs for your offers: the commented NEXT_PUBLIC_STRIPE_*_PRICE_ID* lines for Stripe, or the POLAR_*_PRODUCT_ID* lines for Polar.

Only NEXT_PUBLIC_* settings are exposed to browser code. Database, administrative, payment and email secrets remain server-side. Do not put them in , commit the deployment file, or paste its values into a shell command or support message.

Build and check the artifact

bun --env-file=.env.cloudflare run build:cloudflare
cf deploy --prebuilt --dry-run

The build uses Nitro's Workers preset, checks TypeScript, then prepares the server modules and public assets under .cloudflare/output/ for the Cloudflare CLI. It does not read . The dry run validates the artifact without uploading it. Both commands must finish successfully.

Public values are fixed into the browser bundle during this build. Rebuild whenever the public origin or other NEXT_PUBLIC_* values change.

Deploy the built app

cf deploy --prebuilt --secrets-file .env.cloudflare

The CLI uploads the prepared app and stores the supplied runtime settings as Worker secrets. It prints the deployed URL and version. Confirm the Worker name and URL match your intended app before testing it.

Verify the hosted app

Open the printed URL, then verify the parts of your product that you configured. To sign in, first create an account at /signup with the email address of a member of your Supabase project team. Supabase's default email service sends confirmation email only to those addresses until you configure custom SMTP.

CheckExpected result
Home, pricing, Docs and BlogPages and assets load on a direct visit and refresh.
/api/healthHTTP 200 with status: ok. This checks liveness only.
Sign in, reload, sign outThe session survives reload and protected pages require signin after logout.
Create a ProjectThe Project remains after refresh in the same Organization.
Your selected billing flow, if configuredTest checkout returns to the Worker origin and payment grants access.
Your application's social imageThe emitted image URL returns an image rather than an error page.

A successful deployment or health response does not prove authentication, database writes, payment handling or email delivery. Stop on a failed check; inspect the affected Worker request in Cloudflare logs before changing provider credentials or retrying a payment.

Update or recover

Rebuild and rerun the dry run before deploying a change. Keep the previous Cloudflare version until the new one passes your checks; use Cloudflare's rollback procedure if a deployed change fails. Do not reset Supabase to repair a Worker deployment.

What's next

Was this page helpful?

On this page