Promote Deployment

Two-Stage Model

feature branch → staging (preview) → main (production)
  • staging: Integration branch. Vercel deploys a preview against Neon staging database.
  • main: Production. Vercel deploys to production against Neon main database.

Usage

Promote to staging (from a feature branch):

./scripts/promote.sh staging

Promote to production (from staging):

./scripts/promote.sh production

The script runs quality gates automatically, checks for uncommitted changes, and creates a PR via gh.

Skill Web

This skill is the release coordinator. Use it first for promotion work, then pull in the domain skill that matches the layer that is failing:

  • vercel-deployment
    • Vercel deployment state
    • branch-specific deploy failures
    • redeploy behavior
    • alias cutover issues
  • github-actions
    • CI workflow failures
    • promotion workflow debugging
    • release job behavior
  • better-auth-best-practices
    • auth/session failures seen during deploy smoke
  • organization-best-practices
    • org-scoping, org bootstrap, or membership issues seen during deploy smoke

Do not jump into those skills first if the problem might simply be release ordering or environment drift. Start here, isolate the failing layer, then branch out.

Prefer Vercel CLI First

For release work in this repo, prefer the Vercel CLI over manual dashboard clicking when the CLI can answer the question.

Most useful commands here:

  • Inspect recent deployments:
    • vercel list --meta githubCommitRef=<branch>
    • vercel list --meta githubCommitSha=<sha>
  • Wait for a deployment and inspect logs:
    • vercel inspect <deployment-url-or-id> --wait
    • vercel inspect <deployment-url-or-id> --logs --wait
  • Rebuild an existing deployment:
    • vercel redeploy <deployment-url-or-id>
    • vercel redeploy <deployment-url-or-id> --target=preview
    • vercel redeploy <deployment-url-or-id> --target=production
  • Pull env or run commands with env:
    • vercel env pull .env.preview-branch --environment preview --git-branch <branch>
    • vercel env run -- <command>
  • Pull local project settings for local replication:
    • vercel pull
    • vercel pull --environment=production
  • Production deployment control:
    • vercel promote <deployment-url-or-id>
    • vercel rollback <deployment-url-or-id>

Use the dashboard when it adds context, not as the default control surface.

Happy Path

Prefer this exact order:

  1. Run repo gates.
  2. Push the branch and let Vercel build the preview deployment if branch validation is the goal.
  3. Verify the exact git ref being promoted.
  4. Check Vercel deployment state for that exact ref and wait for the preview deployment to exist before continuing with branch-specific env or DB checks.
  5. Use vercel env pull only after the preview deployment exists if you need to discover branch DB wiring:
    • vercel env pull .env.preview-branch --environment preview --git-branch <branch>
  6. If direct DB access is needed after that, use neonctl.
  7. Verify Neon branch mapping with neonctl.
  8. Verify Vercel env parity:
    • bun run deploy:check-env-parity -- --environment staging --neon-project-id <id>
    • bun run deploy:check-env-parity -- --environment production --neon-project-id <id>
  9. Rebuild local release artifacts used by smoke:
    • cd cli && make build
  10. Provision a real smoke identity:
  • bun run smoke:provision -- --environment staging
  • bun run smoke:provision -- --environment production
  1. Run deployed smoke against the actual hosted URL.
  2. Only then treat the release as valid.

If any step fails, stop and fix that layer before continuing.

Manual Promotion Checklist

If the script doesn't fit the situation, follow this sequence exactly:

  1. Generate migrations: bunx drizzle-kit generate (if schema changed)
  2. Validate migrations: bun run db:check
  3. Run all gates: ./scripts/gates.sh
  4. Open PR to staging
  5. Verify staging env parity against Neon staging
  6. Validate preview deployment against Neon staging
  7. Open promotion PR from stagingmain
  8. Verify production env parity against Neon main
  9. Migrations apply automatically during Vercel build via DATABASE_URL_UNPOOLED
  10. If migration fails, STOP. Do not retry. Fix the schema/migration drift first.

Database Safety

  • Migrations run during Vercel's build step using DATABASE_URL_UNPOOLED (direct connection).
  • For hosted preview, staging, and production validation, prefer remote deploys over local db:push or direct DDL.
  • Do not apply migrations manually to shared or preview branches. Let the Vercel build step apply them during remote deploy.
  • Do not treat branch env or branch DB state as stable until the preview deployment for that branch actually exists.
  • Never assume the deployed app is pointed at the correct Neon branch because a previous release worked.
  • If you need direct DB access for a specific preview branch, first discover the wiring via:
    • vercel env pull .env.preview-branch --environment preview --git-branch <branch>
  • Then use neonctl for the actual DB access path.
  • Verify shared-environment routing with:
    • neonctl branches list --project-id <id>
    • neonctl connection-string staging --project-id <id>
    • neonctl connection-string main --project-id <id>
  • Verify Vercel env routing with:
    • bun run deploy:check-env-parity -- --environment staging --neon-project-id <id>
    • bun run deploy:check-env-parity -- --environment production --neon-project-id <id>
  • See the db-health skill for full database safety rules.

Environment Mapping

Git BranchVercel EnvironmentNeon Branch
stagingPreviewstaging
mainProductionmain
feature branchesPreview (PR)preview/* (auto-forked)

Guardrails

  • Never push directly to main. Always go through staging first.
  • Never skip quality gates to speed up deployment.
  • Never skip env-parity checks for shared-environment promotion.
  • Never use drizzle-kit push or bun run db:push as a shortcut for hosted preview, staging, or main.
  • Treat migration failures as hard blockers — no retry without a fix.
  • Treat deploy smoke failures as hard blockers.
  • Treat stale local release artifacts as hard blockers for smoke.
  • Never run drizzle-kit push against any shared branch.

Common Pitfalls

  • PR preview success does not prove staging or main deploy success.
  • Vercel author/collaboration failures can be branch-specific.
  • Production can be healthy but still point at Neon staging.
  • Branch-specific database access should come from vercel env pull, not guessed endpoints or copied local env files.
  • vercel env pull can race a brand-new branch push; wait for the preview deployment first.
  • Direct DB access should go through neonctl, not copied connection strings.
  • Live preview URL validation is stronger than local DB poking when the goal is release confidence.
  • A stale local cli/bin/seed can make deploy smoke fail even when the deployed API is correct.
  • The release is not complete until the public alias serves the intended deployment id.

If You're Lost