部署故障排查
排查环境、数据库、认证、Webhook、cron 和 Vercel 部署故障。
先从实际失败的执行面入手。本地构建、Vercel 部署、服务商 Webhook 和定时任务使用不同的配置,也会产生不同的证据。
关联了错误的 Vercel 项目
请在部署前停止。检查 .vercel/project.json,确认项目和团队都正确;任一项错误,都应重新运行固定版本的 vercel link 流程。不要把另一个应用的 .vercel/ 目录复制到当前检出目录。
生产环境使用了本地值或旧值
基线部署封装脚本读取 .env.production.local,不会读取你最近在 .env.local 中编辑的值。
npx --yes vercel@54.21.1 env pull .env.production.local --environment=production
npm run deploy:vercel每次修改 Vercel 环境变量后都要重新拉取。公开的 NEXT_PUBLIC_* 值会写入客户端构建,因此变更后必须重新部署。
预检拒绝站点 URL
NEXT_PUBLIC_SITE_URL 必须是有效的 HTTP 或 HTTPS URL,且不能指向 localhost、127.0.0.1 或 ::1。在生产环境中,除非你有意使用不同源地址,否则应让 BETTER_AUTH_URL、NEXT_PUBLIC_SITE_URL 和 NEXT_PUBLIC_APP_URL 都指向目标源地址。
预检通过,但其他门禁失败
这是正常且可能发生的情况。预检只检查能力配置、数据库可达性、特定迁移阻塞项和 TypeScript;不会运行 lint、单元测试、集成测试或生产构建。请直接运行失败的命令并修复对应结果,不要把预检通过当成整个仓库已经通过验收。
db:push 失败
这是预期行为。Starter 禁止直接推送数据库结构。请生成、审查并执行迁移:
npm run db:generate
npm run db:migrate不要直接调用 drizzle-kit push。
数据库连接失败
- 确认
DATABASE_URL指向目标数据库,并包含必需的 TLS 参数。 - 确认数据库接受来自当前网络或 Vercel 区域的连接。
- 检查连接池限制和服务商维护状态。
- 使用
npm run preflight测试;它会设置十秒连接超时并执行简单查询。 - 切勿把连接 URL 粘贴到问题单或聊天记录中。
迁移安全检查报告重复数据
预检会检查重复的订阅用户、积分交易来源和 Webhook 服务商事件 ID,因为已提交的唯一索引依赖这些不变量。执行迁移前,请检查并妥善处理报告中的数据行。不要盲目删除记录,也不要手动把迁移标记为已应用。
迁移已在本地执行,但生产环境没有执行
生产迁移执行器只会执行已提交到 drizzle/*.sql、且目标数据库 app_migrations 表中尚未记录的文件。请确认:
- SQL 文件已经提交,并包含在部署版本中;
- 部署使用目标
DATABASE_URL; - 文件名没有被复用,文件也没有在之前执行后被修改;
- 没有其他迁移进程同时运行。
认证只在生产环境失败
- 生产环境至少要启用一种完整的认证方式:邮箱密码或 Google OAuth。
- 邮箱密码认证需要
NEXT_PUBLIC_ENABLE_EMAIL_AUTH=true和正常工作的邮件投递。 - 受支持的 Vercel 基线要求使用 Resend 和已验证域名下的
RESEND_FROM_EMAIL。 - Google OAuth 需要两项服务端凭据;One Tap 还需要
NEXT_PUBLIC_GOOGLE_CLIENT_ID。 - 确认服务商回调 URL 使用的源地址与
BETTER_AUTH_URL一致。 - 生产环境的安全 Cookie 需要 HTTPS。
收不到验证或密码重置邮件
检查 Resend 投递日志、发件域名验证、收件人状态和 Vercel 函数日志。如果你在基线路径之外使用 SMTP,必须完整配置 SMTP 主机、端口、用户名、密码和发件地址。分享诊断信息前,请移除凭据和邮件中的链接。
基线部署拒绝已启用的支付
npm run deploy:vercel 有意要求 NEXT_PUBLIC_ENABLE_PAYMENTS=false。它只覆盖经过验证的最小基线,不是通用的可选能力部署封装脚本。请参阅部署到 Vercel,不要通过隐藏开关来绕过检查。
支付预检报告产品目录缺失
启用支付后,预检要求所选服务商的凭据、Webhook 密钥,以及所有内置且可见的 Starter/Pro 月付与年付方案和小、中、大积分包。如果你的产品只展示更小的目录,请同时更新产品目录及其就绪性契约;不要让 UI 指向未配置的标识符。
密钥、Price/Product ID、Webhook 密钥和端点必须全部处于测试模式,或全部处于生产模式。
Webhook 返回 400
缺少签名或签名无效。请确认:
- 服务商发送到
/api/payments/webhook; PAYMENTS_PROVIDER与发送方一致;- 签名密钥属于这个准确的端点和环境;
- 没有代理或处理器修改原始请求体。
Webhook 返回 503
所选服务商配置不完整。请在同一目标环境运行预检,并补齐报告中的全部变量。非当前服务商的凭据不能让当前服务商进入就绪状态。
Webhook 返回 500
验签成功,但应用处理失败。检查应用错误,修复数据库或领域逻辑故障,然后回放原始服务商事件。Webhook 收件箱会尽可能释放失败任务,也可以重新取得已过期且尚未处理的租约。
回放返回 deduped: true
该投递已处理,或当前由另一次尝试持有。再次回放前,请检查 webhook_inbox 和受影响的业务表。反复重定向或反复发送 Webhook 不能替代数据库回读。
Cron 返回 401、503 或 500
| 状态 | 检查项 |
|---|---|
401 | Vercel 发送的 CRON_SECRET 与路由预期一致 |
503 | 目标环境中存在 CRON_SECRET |
500 | 日志、订阅方案 ID 和数据库写入 |
没有订阅边界到期时,任务成功但发放数为零是正常结果。任务每天运行,但只会在每月订阅边界发放积分。
存储或 AI 能力未通过预检
对象存储需要受支持的服务商(r2、s3 或 minio),并完整配置端点、访问密钥 ID、私有访问密钥和存储桶。AI 图片生成需要启用功能开关,并配置 Fal、ModelScope 或 DashScope 服务商。对于尚未完成的能力,应保持关闭,不要部署不完整的凭据。
不同部署的限流行为不一致
内置限流器把计数器保存在单个进程中。它适用于本地行为和基础请求整形,但在 Vercel、多实例或负载均衡部署中不能作为生产级分布式限流器。依赖限流防止滥用之前,请替换为共享且兼容无服务器架构的存储。
有关命令和环境的详细说明,请参阅命令与环境变量。