Skip to content

Deployment

React Starter Kit deploys as three Cloudflare Workers backed by a Neon PostgreSQL database. Infrastructure is managed with Terraform.

What Gets Deployed

ComponentTargetDescription
Web WorkerCloudflare WorkersEdge router – receives all traffic, routes to app/api via service bindings
App WorkerCloudflare WorkersServes the React SPA and static assets
API WorkerCloudflare WorkersHono + tRPC server, authentication, database access
DatabaseNeon PostgreSQLManaged Postgres with Hyperdrive connection pooling
InfrastructureTerraformHyperdrive configurations, optional R2 storage

See Architecture Overview for how these components connect.

Prerequisites

  • Cloudflare account with Workers enabled
  • Neon account for PostgreSQL hosting (sign up)
  • Terraform installed (brew install terraform or download)
  • Domain added to Cloudflare DNS (optional for initial setup)

Environments

EnvironmentIntended triggerURL patternPurpose
Developmentbun devlocalhost:5173Local development
StagingPush to mainstaging.example.comPre-production validation
ProductionManual dispatchexample.comLive environment

Staging and production each have their own Wrangler configuration, Hyperdrive bindings, and Terraform workspace. Development is local and provisions no cloud resources. See CI/CD for deployment triggers. Automated deploys stay off until you set the DEPLOY_ENABLED repository variable, so a fresh clone runs CI only.

Deployment Checklist

  1. Provision infrastructure – run Terraform to create the Hyperdrive configurations. Workers, routes and the custom domain's DNS come from Wrangler at deploy time (ADR-002)
  2. Set secrets – configure BETTER_AUTH_SECRET and RESEND_API_KEY, plus secrets for any optional integrations you enable, via Wrangler. See Cloudflare Workers for the full list
  3. Build and verify – compile every workspace, prove each worker bundles, and confirm the Cloudflare identity and production account, all before anything changes
  4. Run migrations – apply the schema to your production database. See Production Database
  5. Deploy the workersapi, then app, then web. Service bindings resolve by name at deploy time, and web holds the public route, so it flips last

Steps 3 to 5 are what an automated release does; push to main or dispatch a production run and CI/CD handles them. To do it by hand:

bash
# Preflight – nothing here changes production
bun run build

bun wrangler deploy --config apps/api/wrangler.jsonc --env="" --dry-run
bun wrangler deploy --config apps/app/wrangler.jsonc --env="" --dry-run
bun wrangler deploy --config apps/web/wrangler.jsonc --env="" --dry-run

# `--dry-run` never authenticates, so confirm the account separately
bun wrangler whoami --account <production-account-id>

Stop unless every command succeeded and whoami reports the account you meant. These are two blocks rather than one because an interactive shell does not stop on error – pasted together, a failed dry-run would scroll past and the migration would still run.

bash
# Point of no return
bun db:migrate:production

bun api:deploy --env=""
bun app:deploy --env=""
bun web:deploy --env=""

The order matters for the same reason it does in CI: migrations run against workers that are still the old ones, so a schema change has to be additive until the new workers are live.

Section Pages