This repo uses one database shape across environments, but different connection strategies for runtime and migrations.
- Database: local Docker Postgres
- Runtime URL:
DATABASE_URL - Migration URL:
DATABASE_MIGRATION_URL - Default rule: if
DATABASE_MIGRATION_URLis empty, Drizzle tooling falls back toDATABASE_URL
- Database: reuse the Supabase staging project
- Web runtime: non-production Vercel envs
- API runtime: preview apps continue talking to staging-backed services
- Do not provision isolated preview databases in this phase
- Database: dedicated Supabase staging project
- Web runtime
DATABASE_URL: Supabase transaction pooler - API runtime
DATABASE_URL: pooled or direct connection string that works for the staging Railway runtime - GitHub migration
DATABASE_MIGRATION_URL: Supabase session pooler on port5432by default, or direct only if the runner can reach it
- Database: dedicated Supabase production project
- Web runtime
DATABASE_URL: Supabase transaction pooler - API runtime
DATABASE_URL: pooled or direct connection string that works for the production Railway runtime - GitHub migration
DATABASE_MIGRATION_URL: Supabase session pooler on port5432by default, or direct only if the runner can reach it
DATABASE_URLis runtime-onlyDATABASE_MIGRATION_URLis migration-only- Vercel web should use the Supabase transaction pooler because Better Auth and the web runtime run there
- Railway API may use a working pooled or direct connection string for runtime traffic
- GitHub migration workflows should use the Supabase session pooler by default because GitHub-hosted runners may not be able to reach Supabase direct IPv6-only endpoints
- Generate schema changes locally with:
pnpm auth:generatewhen Better Auth schema changespnpm db:generatefor Drizzle SQL artifacts
- Commit generated migration files
- Let CI verify migrations on ephemeral Postgres
- Trigger
Database Migrateforstaging - Verify staging app flows
- Trigger
Database Migrateforproductionafter approval - Verify production app flows
Do not auto-run production migrations from deploy hooks in this phase.
- Prefer backup or point-in-time recovery over ad-hoc reverse SQL
- Treat every production migration as forward-only unless a safe reverse migration is explicitly written and reviewed
- If a migration fails partway through, stop and inspect the target database before retrying
Before running a staging or production migration:
- Confirm the target GitHub Environment has the correct
DATABASE_MIGRATION_URL - Confirm the migration files in
packages/db/drizzleare committed - Confirm CI is green, especially
db-verify - Confirm staging and production runtime envs still point at their intended database projects
- Confirm preview still points at staging, not production
Before production migration:
- Confirm backups or point-in-time recovery are available in the production Supabase project
- Confirm the recovery window is acceptable for the change
- Record the migration start time and deployment identifier for incident response
- Vercel owns runtime
DATABASE_URLfor the web app - Railway owns runtime
DATABASE_URLfor the API - GitHub Environment secrets own
DATABASE_MIGRATION_URLfor staging and production migration workflows - Supabase owns the actual database projects and connection string issuance