跳转到主要内容
Vercel 是阻力最小的路径。仓库默认配置(Next.js 15、pnpm、Node runtime)和 Vercel 预设完全吻合, 导入即用,前提是环境变量已经填好。从 git push 到上线大约 5 分钟,大头是粘 secrets 和建数据库。

前置条件

  • 一个带连接池的 Postgres 数据库。Neon、Supabase、Railway、Crunchy 都行。Serverless 函数 不维持长连接,所以必须用 pooler URL(Supabase 的 6543 端口、Neon 的 -pooler 主机名)。
  • 一个 32 位以上的随机 BETTER_AUTH_SECRETopenssl rand -base64 32
  • 当前支付 provider 的密钥。siteConfig.payment.provider 选哪个,对应的 *_SECRET_KEY*_WEBHOOK_SECRET 和相关 *_PRICE_* id 就要填齐。
  • 可选但建议:RESEND_API_KEY 用于事务邮件。没有的话邮件 facade 静默 no-op,注册会跳过验证步骤。
  • Vercel 能读到的 GitHub 仓库。SSO、monorepo、fork 都行。
push 之前本地跑一遍 pnpm typecheck && pnpm lint && pnpm build。CI 会跑同样三条; Vercel 也会在同一关卡拒掉坏 build。

步骤

1. Push 到 GitHub

git push origin main
私有仓库需要在 GitHub app 设置里授权 Vercel。

2. 在 Vercel 导入

Vercel dashboard:Add New → Project → 选仓库。Framework Preset 自动识别为 Next.js。 build command 留空(Vercel 从 package.jsonpnpm build)。Output directory 是 .next(默认)。

3. 填环境变量

Settings → Environment Variables。必填基线:
DATABASE_URL=postgres://user:pass@host:6543/db?sslmode=require
BETTER_AUTH_SECRET=<32+ 随机字符>
BETTER_AUTH_URL=https://your-domain.com
NEXT_PUBLIC_APP_URL=https://your-domain.com
[email protected]
加上当前激活支付 provider 的密钥(Stripe 示例):
STRIPE_SECRET_KEY=sk_live_…
STRIPE_WEBHOOK_SECRET=whsec_…
STRIPE_PRICE_VIBESTRAP_PROMO=price_…
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_…
可选但常用:RESEND_API_KEYGOOGLE_CLIENT_ID/SECRETTURNSTILE_SECRET_KEY + NEXT_PUBLIC_TURNSTILE_SITE_KEY。完整清单见 Env 参考

4. 同步生产数据库

首次部署前先把 schema 同步过去。两种方式:
# 方案 A —— 最快,dev 风格。把 schema 与 DB diff 后直接执行。
DATABASE_URL=<prod-url> pnpm db:push
# 方案 B —— 提交式 migration。一旦有真实数据就用这个。
pnpm db:generate            # 在 drizzle/ 下写一份 SQL 文件
git add drizzle/ && git commit
DATABASE_URL=<prod-url> pnpm db:migrate
一旦有付费用户,绝对不要再对生产跑 db:push——它会无声丢列。改用方案 B,从 deploy hook 或一次性脚本跑 db:migrate,别从运行中的 app 跑。

5. 配置支付 webhook

在支付 provider 的 dashboard,加 webhook endpoint,指向:
https://your-domain.com/api/webhooks/stripe
https://your-domain.com/api/webhooks/paddle
https://your-domain.com/api/webhooks/lemonsqueezy
https://your-domain.com/api/webhooks/creem
挑你激活 provider 对应的那条。订阅:checkout.session.completedinvoice.paidcustomer.subscription.updatedcustomer.subscription.deleted(Stripe 命名—— 其他 provider 自行对应)。把签名密钥贴回 STRIPE_WEBHOOK_SECRET(或对应变量)。

6. Deploy

push 或点 Deploy。首次 build 2-3 分钟。Vercel 的日志会大声报 env 错误——src/env.ts 会在 启动时对必填项缺失抛错。

验证

逐个访问下面的 URL,红的就是哪里没配好。
  • https://your-domain.com/ —— 首页正常,hero 是你设的语言
  • https://your-domain.com/zh/ —— 中文 hero
  • https://your-domain.com/api/ping —— 返回 { ok: true }
  • https://your-domain.com/sitemap.xml —— 所有公共路由都列出来
  • https://your-domain.com/robots.txt —— /admin/api//settings 全 disallow
  • https://your-domain.com/register —— 注册可用,能收到验证邮件
然后用 live key 跑一笔真实的 $1 充值(之后退给自己):paymentcredit_transaction 两张 表应该在结账后 5 秒内各出现一行。如果没出现,去支付 dashboard 的 webhook 日志看 4xx 响应。

常见坑

  • BETTER_AUTH_URL 不一致。 必须是带 scheme 的完整 prod URL,不带末尾斜杠。和实际域名 漂移时认证回调会静默失败。
  • prod 缺环境变量。 Vercel 把 Production / Preview / Development 分开。除非你刻意要分环境 值,否则统一勾「All Environments」。
  • Webhook 签名密钥不对。 旧的 STRIPE_WEBHOOK_SECRET 会让每个事件返回 400。在 dashboard 轮换一次密钥,贴回 Vercel,重新部署。
  • Postgres 连接数撑满。 Serverless 函数烧连接很快。用 pooler URL(Supabase 6543 端口、 Neon 的 -pooler.region 主机)。撞「max connections」?你用的是直连 URL。
  • 忘了 NEXT_PUBLIC_APP_URL 被 sitemap、OG 图、OAuth redirect、支付成功 URL 用。 生产环境必须和 BETTER_AUTH_URL 一致。
  • 首次部署前没跑 db:push App 起得来,第一个 query 就 500,因为表不存在。

回滚

Vercel 永久保留每次部署。从 Deployments 列表把任意旧 build promote 回去——秒级回滚。 如果是 migration 的锅,用 DB provider 的 point-in-time 快照恢复(Neon 和 Supabase 所有套餐 都自带)。

官方文档