> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vibestrap.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Deploy to Vercel

> Five-minute Vercel deploy — push, set env, hit deploy.

Vercel is the path of least resistance. The repo's preset matches Vercel's defaults
(Next.js 15, pnpm, Node runtime), so a fresh project import "just works" once env
vars are in place. Plan on 5 minutes from `git push` to live URL — the bulk of the
time is pasting secrets and creating the database.

## Prerequisites

* A Postgres database with a **pooled** connection string. Neon, Supabase, Railway,
  Crunchy — any of them. Serverless functions don't keep connections warm, so the
  pooler URL (port 6543 on Supabase, the `-pooler` host on Neon) is required.
* A `BETTER_AUTH_SECRET` of 32+ random characters: `openssl rand -base64 32`.
* Your active payment provider's keys. Whichever provider you set in
  `siteConfig.payment.provider` needs its `*_SECRET_KEY`, `*_WEBHOOK_SECRET` and
  the relevant `*_PRICE_*` ids populated.
* Optional but recommended: a `RESEND_API_KEY` for transactional email. Without
  one, the mail facade no-ops gracefully and signups skip the verification step.
* A GitHub repo Vercel can read. SSO, monorepo, fork — all fine.

Run `pnpm typecheck && pnpm lint && pnpm build` locally before pushing. CI runs the
same three; Vercel will refuse a broken build at the same gate.

## Step-by-step

### 1. Push to GitHub

```bash theme={null}
git push origin main
```

If you keep a private repo, grant Vercel access in the GitHub app settings.

### 2. Import on Vercel

In the Vercel dashboard: Add New → Project → pick the repo. The Framework Preset
auto-resolves to **Next.js**. Leave the build command blank (Vercel reads
`pnpm build` from `package.json`). Output directory is `.next` (default).

### 3. Set env vars

Settings → Environment Variables. The **required** baseline is:

```
DATABASE_URL=postgres://user:pass@host:6543/db?sslmode=require
BETTER_AUTH_SECRET=<32+ random chars>
BETTER_AUTH_URL=https://your-domain.com
NEXT_PUBLIC_APP_URL=https://your-domain.com
ADMIN_EMAILS=you@example.com
```

Add your **active** payment provider's keys (Stripe shown):

```
STRIPE_SECRET_KEY=sk_live_…
STRIPE_WEBHOOK_SECRET=whsec_…
STRIPE_PRICE_VIBESTRAP_PROMO=price_…
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_…
```

Optional but common: `RESEND_API_KEY`, `GOOGLE_CLIENT_ID/SECRET`,
`TURNSTILE_SECRET_KEY` + `NEXT_PUBLIC_TURNSTILE_SITE_KEY`. See
[Env reference](/env-reference) for the full list.

### 4. Migrate the production database

**Before** the first deploy, sync your schema. You have two options:

```bash theme={null}
# Option A — fastest, dev-only style. Diffs schema against DB and applies.
DATABASE_URL=<prod-url> pnpm db:push
```

```bash theme={null}
# Option B — committed migrations. Use this once you have real data.
pnpm db:generate            # writes a SQL file under drizzle/
git add drizzle/ && git commit
DATABASE_URL=<prod-url> pnpm db:migrate
```

Once you have paying users, **never** run `db:push` against production again — it
can drop columns silently. Switch to Option B and run `db:migrate` from a deploy
hook or a one-shot script, not from your running app.

### 5. Configure payment webhooks

In your payment provider's dashboard, add a webhook endpoint pointing at:

```
https://your-domain.com/api/webhooks/stripe
https://your-domain.com/api/webhooks/creem
https://your-domain.com/api/webhooks/nowpayments
```

Pick the route matching your active provider. Subscribe to:
`checkout.session.completed`, `invoice.paid`, `customer.subscription.updated`,
`customer.subscription.deleted` (Stripe naming — adapt for others). Copy the
signing secret into `STRIPE_WEBHOOK_SECRET` (or equivalent).

### 6. Deploy

Push or click Deploy. The first build takes 2-3 minutes. Vercel's logs surface
env-var errors loudly — `src/env.ts` throws at startup if anything required is
missing.

## Verify it works

Hit each URL once. Anything red means something's misconfigured.

* `https://your-domain.com/` — home renders, hero copy is your locale
* `https://your-domain.com/zh/` — Chinese hero
* `https://your-domain.com/api/ping` — returns `{ ok: true }`
* `https://your-domain.com/sitemap.xml` — every public route listed
* `https://your-domain.com/robots.txt` — disallows `/admin`, `/api/`, `/settings`
* `https://your-domain.com/register` — signup works, you receive the verification email

Then run a real \$1 test charge through your live keys (refund yourself afterwards):
the `payment` and `credit_transaction` rows should both appear within \~5 seconds
of completing checkout. If they don't, check the webhook logs in your payment
dashboard for 4xx responses.

## Common pitfalls

* **`BETTER_AUTH_URL` mismatch.** It must be the full prod URL with scheme, no
  trailing slash. Auth callbacks silently fail when this drifts from the actual
  domain.
* **Missing env vars in prod.** Vercel separates Production / Preview /
  Development scopes. Set vars to "All Environments" unless you know you need
  per-env values.
* **Webhook signing-secret mismatch.** A stale `STRIPE_WEBHOOK_SECRET` returns
  400 on every event. Rotate the secret in the dashboard, paste it into Vercel,
  redeploy.
* **Postgres connection limits.** Serverless functions burn connections fast.
  Use the pooler URL (port 6543 on Supabase, `-pooler.region` host on Neon). Hit
  "max connections" errors? You're using the direct URL.
* **Forgotten `NEXT_PUBLIC_APP_URL`.** Used by sitemap, OG images, OAuth
  redirects, payment success URLs. Must match `BETTER_AUTH_URL` in production.
* **Skipping `db:push` before first deploy.** App boots, then 500s on the first
  query because the tables don't exist.

## Rollback

Vercel keeps every deployment forever. Promote any previous build from the
Deployments list — instant revert. If a migration is the culprit, restore from
your DB provider's point-in-time snapshot (Neon and Supabase both ship this on
every plan).

## Official docs

* Vercel Next.js guide: [vercel.com/docs/frameworks/nextjs](https://vercel.com/docs/frameworks/nextjs)
* Env vars: [vercel.com/docs/projects/environment-variables](https://vercel.com/docs/projects/environment-variables)
* Cron jobs (for credit-expiry sweeps): [vercel.com/docs/cron-jobs](https://vercel.com/docs/cron-jobs)
* Drizzle migrations: [orm.drizzle.team/docs/migrations](https://orm.drizzle.team/docs/migrations)
