主題
Docker 環境測試與部署
完成 MVP 開發後,用 Docker 測試確保應用可以正確打包和部署。
Step 1:更新 next.config.ts
啟用 standalone 輸出模式:
typescript
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
output: 'standalone',
};
export default nextConfig;為什麼需要 standalone?
standalone 模式會產生一個最小化的 Node.js server,包含所有必要的依賴,非常適合 Docker 部署。
Step 2:建立 Dockerfile
在專案根目錄建立 Dockerfile:
dockerfile
FROM node:22-alpine AS base
# 安裝依賴
FROM base AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json pnpm-lock.yaml* ./
RUN corepack enable pnpm && pnpm install --frozen-lockfile
# 建構應用
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# 設定環境變數(建構時)
ENV NEXT_TELEMETRY_DISABLED=1
RUN corepack enable pnpm && pnpm build
# 運行環境
FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
# 設定正確的權限
RUN mkdir .next
RUN chown nextjs:nodejs .next
# 複製 standalone 輸出
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]Step 3:建立 .dockerignore
避免複製不必要的檔案:
# .dockerignore
Dockerfile
.dockerignore
node_modules
npm-debug.log
README.md
.next
.git
.env*.localStep 4:本地測試 Docker
建構映像
bash
docker build -t my-app .運行容器
bash
# 基本運行
docker run -p 3000:3000 my-app
# 帶環境變數運行
docker run -p 3000:3000 \
-e NEXT_PUBLIC_SUPABASE_URL=http://host.docker.internal:54321 \
-e NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key \
my-apphost.docker.internal
在 Docker 中連接本地 Supabase,使用 host.docker.internal 而不是 localhost 或 127.0.0.1。
驗證應用
開啟瀏覽器訪問 http://localhost:3000,確認:
- 頁面正常顯示
- 資料可以讀取
- 表單可以提交
- Auth 功能正常
Step 5:用 docker-compose(可選)
如果需要同時運行多個服務:
yaml
# docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- NEXT_PUBLIC_SUPABASE_URL=${NEXT_PUBLIC_SUPABASE_URL}
- NEXT_PUBLIC_SUPABASE_ANON_KEY=${NEXT_PUBLIC_SUPABASE_ANON_KEY}
depends_on:
- supabase
supabase:
image: supabase/postgres:15.1.0.117
# 這只是示例,實際使用 npx supabase start運行:
bash
docker-compose upStep 6:推送到 GitHub
確認 Docker 測試通過後,推送程式碼:
bash
# 如果還沒初始化 Git
git init
git add .
git commit -m "feat: MVP complete with Docker support"
# 連接 GitHub repo
git remote add origin https://github.com/your-username/your-app.git
git branch -M main
git push -u origin mainStep 7:部署到 Vercel
方法 1:Vercel Dashboard(推薦)
- 前往 vercel.com
- 使用 GitHub 登入
- 點擊「Add New...」>「Project」
- 選擇你的 GitHub repo
- 設定環境變數
- 點擊「Deploy」
方法 2:Vercel CLI
bash
# 安裝 CLI
npm install -g vercel
# 登入
vercel login
# 部署
vercel
# Production 部署
vercel --prod環境變數設定
在 Vercel Dashboard 設定 Production 環境變數:
| 變數名稱 | 值 | 說明 |
|---|---|---|
NEXT_PUBLIC_SUPABASE_URL | https://xxx.supabase.co | Supabase 雲端 URL |
NEXT_PUBLIC_SUPABASE_ANON_KEY | eyJhbGci... | Supabase Anon Key |
取得 Supabase 雲端資訊
- 前往 supabase.com
- 建立新專案或選擇現有專案
- 前往 Settings > API
- 複製 Project URL 和 anon public key
重要
Production 要使用 Supabase 雲端專案,不是本地的!
記得把本地的 migration 推送到雲端:
bash
npx supabase db push持續部署
Vercel 會自動監聽 GitHub 變更:
| 事件 | 行為 |
|---|---|
推送到 main | Production 部署 |
| 推送到其他分支 | Preview 部署 |
| 開 Pull Request | Preview 部署 |
常見問題
Docker build 失敗:pnpm 錯誤
確認 corepack enable pnpm 在 Dockerfile 中:
dockerfile
RUN corepack enable pnpm && pnpm install --frozen-lockfileDocker run 連不到 Supabase
使用 host.docker.internal:
bash
docker run -p 3000:3000 \
-e NEXT_PUBLIC_SUPABASE_URL=http://host.docker.internal:54321 \
my-appVercel build 失敗
- 確認本地
pnpm build成功 - 確認本地
pnpm lint無錯誤 - 檢查環境變數是否正確設定
環境變數沒生效
- 變數名稱大小寫敏感
- 前端用的要加
NEXT_PUBLIC_前綴 - 設定後要重新部署
部署檢查清單
- [ ]
pnpm build成功 - [ ]
pnpm lint無錯誤 - [ ] Docker build 成功
- [ ] Docker run 應用正常
- [ ] Supabase migration 已推送到雲端
- [ ] Vercel 環境變數已設定
- [ ] Production 部署成功
效能優化(可選)
Vercel Analytics
bash
pnpm add @vercel/analyticstypescript
// app/layout.tsx
import { Analytics } from '@vercel/analytics/react';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="zh-TW">
<body>
{children}
<Analytics />
</body>
</html>
);
}Speed Insights
bash
pnpm add @vercel/speed-insightstypescript
// app/layout.tsx
import { SpeedInsights } from '@vercel/speed-insights/next';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="zh-TW">
<body>
{children}
<SpeedInsights />
</body>
</html>
);
}恭喜!
你的應用已經上線了!
記得:Ship first, perfect later
接下來可以:
- 收集使用者回饋
- 迭代改進功能
- 優化效能
- 加入更多功能