從 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-TW/blog/deploy-nextjs-postgresql.md。
無法載入 Markdown,請直接開啟: https://zenavo.app/zh-TW/blog/deploy-nextjs-postgresql.md