从 GitHub 到上线:部署 Next.js、PostgreSQL 和自定义域名
在 Zenavo 上把 Next.js 应用和 PostgreSQL 部署到自己的服务器,配置内网连接、构建期与运行时环境变量、Prisma 数据库迁移、HTTPS 域名与自动部署。
本文亦提供 English、繁體中文、日本語、Español 和 Bahasa Indonesia。
把本地写好的 Next.js 应用部署上线,通常要依次搞定五件事:启动脚本、数据库连接、环境变量(分清构建期与运行期)、数据库表迁移,以及绑定 HTTPS 域名。
这篇文章以一个已有的 Next.js 仓库为例,把它和 PostgreSQL 一起跑在你自己的服务器上。开始前,先连接一台 Linux 服务器到 Zenavo。
1. 检查仓库的 package.json
如果仓库没有自定义 Dockerfile,Zenavo 会用 zbpack 自动识别 Next.js 并构建。确认仓库根目录有锁文件(package-lock.json、pnpm-lock.yaml、yarn.lock 或 bun.lockb),且 package.json 包含标准的生产脚本:
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
},
"engines": {
"node": ">=20"
}
}
上线前先在本地跑一次 npm run build,把 TypeScript 类型错误或构建期报错提前修掉。
2. 部署 PostgreSQL
在 Zenavo 创建项目,选择服务器,然后点击 添加服务 → 数据库 → PostgreSQL,选择版本并部署。
数据库跑在项目私有网络里,默认不向公网暴露端口。服务就绪后,它会向同项目的其他服务共享连接变量;服务名叫 postgresql 时,完整连接串变量名为 POSTGRESQL_CONNECTION_STRING。
3. 从 GitHub 添加 Next.js 应用
在同一个项目里点击 添加服务 → GitHub,授权 Zenavo GitHub App 读取你的仓库,然后选择仓库、分支和根目录(如果是 monorepo,比如放在 apps/web,就把根目录填成 apps/web)。
在环境变量里引用刚才的数据库:
DATABASE_URL=${POSTGRESQL_CONNECTION_STRING}
${POSTGRESQL_CONNECTION_STRING} 是 Zenavo 的跨服务变量引用,运行时会自动展开成真实的内网连接串(如果你改了数据库服务名,以界面上实际提示的变量名为准)。
默认的 next start 会自动读取平台传入的 PORT 环境变量。如果你自己写了 Dockerfile 或自定义 server.js,请确保监听 0.0.0.0 且端口与 Zenavo 服务里配置的 HTTP 端口一致。更多配置见 Next.js 部署文档。
4. 分清 NEXT_PUBLIC_* 与服务端变量
Next.js 有两类环境变量,行为差别很大:
- 服务端变量(如
DATABASE_URL、支付密钥、AI API Key):只在服务端代码中读取,绝不要加NEXT_PUBLIC_前缀。在 Zenavo 的 变量 页修改后,重启服务就会生效。 NEXT_PUBLIC_*变量:在执行next build时会被直接内联进前端 JS 包里。修改NEXT_PUBLIC_*后必须重新构建部署,只重启容器不会改变浏览器里拿到的值。
另外,Zenavo 的构建机在构建阶段连不上你服务器内网里的数据库。因此:
- 不要把
next build写成需要先连生产数据库才能通过的脚本; - 需要读数据库的页面应使用动态渲染(不在构建期预渲染静态页面),或者在数据库不可达时跳过静态预取。
5. 自动执行数据库迁移(以 Prisma 为例)
因为构建期连不上内网数据库,数据库表迁移应该放在容器启动前,而不是 build 脚本里。
使用 zbpack 构建时,可以在 package.json 里加一个 predeploy 脚本,它会在每次容器启动、start 执行之前运行:
{
"scripts": {
"build": "prisma generate && next build",
"predeploy": "prisma migrate deploy",
"start": "next start"
}
}
注意两点:
- 生产环境用
prisma migrate deploy(只应用已提交到仓库的迁移文件),不要用开发环境的prisma migrate dev或db push --force-reset。 - 如果你使用自定义
Dockerfile,zbpack 的predeploy钩子不会自动介入,把npx prisma migrate deploy && npm start写进你的容器启动命令(CMD或ENTRYPOINT)即可。
6. 绑定域名与自动部署
应用启动后,在服务的 网络 页绑定你的域名(如 app.example.com),把 DNS 的 A 记录指向服务器 IP,等待 HTTPS 证书自动签发(详见域名文档)。如果你的应用用了 NextAuth / Auth.js、Clerk 或 OAuth 登录,别忘了同步更新 NEXTAUTH_URL 等站点地址变量以及第三方后台的回调地址。
打开页面,走一遍登录和一条数据的增删改查,确认读写都落进了新的 PostgreSQL。
在服务 设置 中开启 推送到这个分支时自动部署,之后每次 git push 都会触发构建并滚动更新;如果是 monorepo,可以配置监听路径,只在该目录有改动时才触发构建(详见 GitHub 部署文档)。
常见问题
构建日志报错 Can't reach database server at postgresql.zenavo.internal:5432?
你的 build 脚本或某个页面在静态生成(SSG)阶段尝试连接内网数据库。把数据库迁移移到 predeploy 或启动命令里,并给需要查数据库的页面加上 export const dynamic = 'force-dynamic'。
修改了 NEXT_PUBLIC_API_URL 并重启,网页里还是旧地址?
NEXT_PUBLIC_* 是在构建时打包进前端代码的。点击 重新部署(Redeploy) 重新跑一次构建即可。
部署新版本后发现有问题,怎么回退? 在部署历史里可以把应用一键回滚到上一个镜像版本。注意:服务回滚只还原应用镜像和配置,不会回滚数据库。如果新版本包含删列等破坏性数据库迁移,执行前请先在 PostgreSQL 服务的 备份与恢复 里创建一个数据库备份。
准备好仓库后,在 Zenavo 控制台新建项目:先起 PostgreSQL,再连 GitHub 仓库、配好 DATABASE_URL 和域名,几分钟就能跑通从 git push 到上线的完整流程。
这是 coding agent 读到的内容,地址是 https://zenavo.app/zh-CN/blog/deploy-nextjs-postgresql.md。
无法加载 Markdown,请直接打开: https://zenavo.app/zh-CN/blog/deploy-nextjs-postgresql.md