本系列文章将围绕Next.js技术栈,旨在为AI Agent开发者提供一套完整的客户端侧工程实践指南。
再优秀的应用,若无法稳定运行于生产环境,其价值将大打折扣。本章将系统讲解从代码提交到用户访问的完整部署流程,涵盖多种部署方案、自动化流水线及运维监控策略。
部署的本质是将应用部署至持续在线的服务器环境中。与开发环境相比,生产环境对稳定性、安全性、性能的要求显著提升,需要系统化的工程实践支撑。
一、构建产物解析
执行 next build 命令后,Next.js 生成优化后的生产就绪文件:
npm run build
输出示例:
Route (app) Size First Load JS
┌ ○ / 5.2 kB 89.4 kB
├ ○ /about 1.3 kB 85.5 kB
├ ● /blog 4.8 kB 89 kB
├ ● /blog/[slug] 2.1 kB 86.3 kB
└ λ /api/data 0 B 84.2 kB
○ (Static) 预渲染为静态内容
● (SSG) 静态生成(使用 generateStaticParams)
λ (Dynamic) 按需服务端渲染
路由类型说明
| 符号 | 类型 | 说明 | 适用场景 |
|---|---|---|---|
| ○ | Static | 纯静态页面,构建时生成 HTML | 关于我们、联系方式等 |
| ● | SSG | 静态生成但依赖动态数据 | 博客列表、产品目录 |
| λ | Dynamic | 每次请求实时渲染 | 用户仪表板、实时数据 |
.next/ 目录结构
.next/
├── cache/ # 构建缓存(加速二次构建)
├── server/ # 服务端代码和 HTML 模板
├── static/ # 静态资源(JS/CSS/图片)
└── standalone/ # 独立部署包(需开启 output: 'standalone')
二、方案一:Vercel 部署(推荐)
Vercel 是 Next.js 官方推荐的托管平台,提供零配置、全球 CDN、自动 HTTPS的无缝体验。
1. Vercel 核心优势
graph LR
Git[Git 仓库<br/>GitHub/GitLab] -->|推送代码| Vercel[Vercel 平台]
Vercel -->|自动构建| Build[next build]
Build -->|全球分发| Edge[边缘节点 CDN]
Edge -->|低延迟访问| User[终端用户]
PR[Pull Request] -->|自动创建| Preview[预览环境]
Preview --> Review[团队审查]
主要优势:
- 零配置部署: 自动识别 Next.js 项目并应用最优配置
- 全球 CDN: 静态资源分发至全球边缘节点,降低延迟
- Edge Functions: 中间件和 API 路由运行在边缘网络
- 预览部署: 每个 PR 自动生成独立预览链接
- 免费额度充足: 个人项目和小团队基本无需付费
2. 部署步骤
第一步:连接代码仓库
- 访问 vercel.com,使用 GitHub 账号登录
- 点击 "New Project"
- 选择要部署的 Next.js 仓库
- 确认框架检测为 "Next.js"
第二步:配置环境变量
在项目设置 → Environment Variables 中添加:
DATABASE_URL=postgresql://user:pass@host:5432/dbname
NEXTAUTH_SECRET=your-super-secret-key-min-32-chars
NEXTAUTH_URL=https://your-domain.com
RESEND_API_KEY=re_xxxxxxxxxxxx
⚠️ 安全警告: 永远不要将包含敏感信息的
.env.local提交到 Git 仓库。Vercel 的环境变量在平台单独管理,与代码隔离。
第三步:推送代码触发部署
git add .
git commit -m "feat: 添加新功能"
git push origin main
# Vercel 自动检测推送,开始构建和部署
2. vercel.json 高级配置
通过 vercel.json 实现精细化控制:
{
"version": 2,
// 指定部署区域
"regions": ["sin1", "hkg1"],
// 自定义响应头
"headers": [
{
"source": "/api/(.*)",
"headers": [
{ "key": "Access-Control-Allow-Origin", "value": "*" },
{ "key": "Access-Control-Allow-Methods", "value": "GET, POST, PUT, DELETE, OPTIONS" },
{ "key": "X-Frame-Options", "value": "DENY" }
]
}
],
// URL 重定向
"redirects": [
{
"source": "/old-blog/:slug",
"destination": "/blog/:slug",
"permanent": true
}
],
// URL 重写(反向代理)
"rewrites": [
{
"source": "/api/proxy/:path*",
"destination": "https://internal-api.example.com/:path*"
}
]
}
三、方案二:Docker 容器化部署
当需要完全掌控基础设施(出于安全、合规或成本考虑),Docker 是首选方案。
1. Docker 核心价值
问题场景: 本地开发环境运行正常,部署到服务器却报错——通常是环境差异导致。
解决方案: Docker 通过容器技术将应用及其运行环境打包,实现"一次构建,到处运行"。
2. 启用 Standalone 模式
// next.config.ts
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
output: 'standalone',
// standalone 模式将所有依赖打包至 .next/standalone
// 不再需要 node_modules,镜像体积大幅减小
};
export default nextConfig;
3. 多阶段 Dockerfile
# Dockerfile
# ==================== 第一阶段:安装依赖 ====================
FROM node:22-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --only=production
# ==================== 第二阶段:构建应用 ====================
FROM node:22-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
# ==================== 第三阶段:生产镜像(最小化) ====================
FROM node:22-alpine AS runner
WORKDIR /app
# 创建非 root 用户(安全最佳实践)
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
# 复制构建产物
COPY --from=builder /app/public ./public
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"]
多阶段构建优势:
- 最终镜像仅包含运行时必需文件
- 镜像体积从 1GB+ 缩减至 ~100MB
- 显著减少攻击面和传输时间
4. Docker Compose 编排
# docker-compose.yml
version: '3.8'
services:
# Next.js 应用
app:
build:
context: .
dockerfile: Dockerfile
ports:
- '3000:3000'
environment:
- NODE_ENV=production
- DATABASE_URL=${DATABASE_URL}
- NEXTAUTH_SECRET=${NEXTAUTH_SECRET}
- NEXTAUTH_URL=${NEXTAUTH_URL}
depends_on:
db:
condition: service_healthy
restart: unless-stopped
networks:
- app-network
# PostgreSQL 数据库
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U ${DB_USER}']
interval: 10s
timeout: 5s
retries: 5
networks:
- app-network
# Nginx 反向代理
nginx:
image: nginx:alpine
ports:
- '80:80'
- '443:443'
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./certs:/etc/nginx/certs:ro
depends_on:
- app
networks:
- app-network
volumes:
postgres_data:
networks:
app-network:
driver: bridge
5. Nginx 反向代理配置
# nginx.conf
events {
worker_connections 1024;
}
http {
# 上游 Next.js 服务
upstream nextjs {
server app:3000;
keepalive 32;
}
# HTTP → HTTPS 重定向
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
return 301 https://$host$request_uri;
}
# HTTPS 服务器
server {
listen 443 ssl http2;
server_name yourdomain.com;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# 静态资源长缓存(文件名含哈希)
location /_next/static/ {
alias /app/.next/static/;
expires 365d;
add_header Cache-Control "public, immutable";
}
# 公共资源
location /public/ {
alias /app/public/;
expires 30d;
}
# 动态内容反向代理
location / {
proxy_pass http://nextjs;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
}
}
四、方案三:Node.js 自托管
适用于拥有 VPS 或云服务器,且希望简化部署流程的场景,通常使用PM2来管理应用。
1. PM2 进程管理
PM2 是 Node.js 最流行的进程管理器,提供自动重启、集群模式、日志管理等功能。
# 全局安装 PM2
npm install -g pm2
# 构建项目
npm run build
# 启动应用
pm2 start npm --name "my-nextjs-app" -- start
# 设置开机自启
pm2 startup
pm2 save
2. PM2 配置文件
// pm2.config.js
module.exports = {
apps: [
{
name: 'my-nextjs-app',
script: 'node_modules/.bin/next',
args: 'start',
instances: 'max',
exec_mode: 'cluster',
watch: false,
max_memory_restart: '1G',
env: {
NODE_ENV: 'production',
PORT: 3000,
},
error_file: './logs/err.log',
out_file: './logs/out.log',
log_date_format: 'YYYY-MM-DD HH:mm:ss',
},
],
};
pm2 start pm2.config.js
pm2 status
pm2 logs my-nextjs-app
pm2 reload my-nextjs-app
五、GitHub Actions 自动化 CI/CD
手动部署效率低下且易出错。CI/CD 流水线实现:推送代码即自动完成质量检查、构建和部署。
graph TB
Push[开发者推送代码] --> GHA[GitHub Actions 触发]
GHA --> Lint[代码检查<br/>ESLint + TypeScript]
Lint --> Test[单元测试<br/>Vitest/Jest]
Test --> Build[构建应用<br/>next build]
Build --> Deploy{分支判断}
Deploy -->|main| Prod[生产环境]
Deploy -->|feature/*| Preview[预览环境]
Prod --> Notify[通知团队]
1. Vercel 部署流水线
# .github/workflows/deploy.yml
name: Deploy to Vercel
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
quality:
name: Code Quality
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: TypeScript type check
run: npx tsc --noEmit
- name: ESLint
run: npm run lint
- name: Run tests
run: npm test
deploy:
name: Deploy
runs-on: ubuntu-latest
needs: quality
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Deploy to Vercel Production
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.ORG_ID }}
vercel-project-id: ${{ secrets.PROJECT_ID }}
vercel-args: '--prod'
2. 自托管服务器部署
# .github/workflows/self-hosted-deploy.yml
name: Deploy to Server
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
- name: Install & Build
run: |
npm ci
npm run build
- name: Deploy via SSH
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /var/www/my-app
git pull origin main
npm ci --only=production
npm run build
pm2 reload my-nextjs-app
echo "✅ Deployment completed at $(date)"
3. GitHub Secrets 配置
在仓库 Settings → Secrets and variables → Actions 中添加:
| Secret 名称 | 说明 | 获取方式 |
|---|---|---|
VERCEL_TOKEN | Vercel API Token | Vercel Dashboard |
ORG_ID | Vercel 组织 ID | Vercel Dashboard |
PROJECT_ID | Vercel 项目 ID | Vercel Dashboard |
SERVER_HOST | 服务器 IP/域名 | 你的服务器地址 |
SERVER_USER | SSH 用户名 | 服务器用户 |
SSH_PRIVATE_KEY | SSH 私钥 | cat ~/.ssh/id_rsa |
DATABASE_URL | 数据库连接串 | 数据库提供商 |
六、环境变量安全管理
N1. ext.js 环境变量规则
.env # 所有环境共享(极少使用)
.env.development # 仅开发环境
.env.production # 仅生产环境
.env.local # 本地覆盖(不提交 Git,优先级最高)
2. 客户端 vs 服务端变量
# ✅ 仅服务端可用(不会暴露给浏览器)
DATABASE_URL=postgresql://user:pass@host/db
NEXTAUTH_SECRET=super-secret-string
API_KEY=sk-xxxxxxxxx
# ⚠️ NEXT_PUBLIC_ 前缀会打包进客户端 JS(用户可见)
NEXT_PUBLIC_API_URL=https://api.example.com
NEXT_PUBLIC_ANALYTICS_ID=UA-XXXXX
⚠️ 严重安全警告: 绝对禁止将数据库密码、API 密钥等敏感信息放在
NEXT_PUBLIC_前缀的变量中!
3. 类型安全的环境变量(推荐)
使用 Zod 进行运行时验证:
// lib/env.ts
import { z } from 'zod';
const envSchema = z.object({
DATABASE_URL: z.string().url(),
NEXTAUTH_SECRET: z.string().min(32),
NEXTAUTH_URL: z.string().url(),
RESEND_API_KEY: z.string().startsWith('re_'),
NEXT_PUBLIC_APP_URL: z.string().url(),
});
export const env = envSchema.parse(process.env);
// 使用时有类型提示且确保值存在
import { env } from '@/lib/env';
const response = await fetch(`${env.NEXT_PUBLIC_APP_URL}/api/data`);
七、生产环境监控
1. 健康检查接口
// app/api/health/route.ts
import { NextResponse } from 'next/server';
export async function GET() {
const checks = {
timestamp: new Date().toISOString(),
status: 'healthy',
version: process.env.npm_package_version || 'unknown',
uptime: process.uptime(),
};
try {
// await db.query('SELECT 1');
return NextResponse.json(checks, { status: 200 });
} catch (error) {
return NextResponse.json(
{
...checks,
status: 'unhealthy',
error: 'Database connection failed'
},
{ status: 503 }
);
}
}
2. Sentry 错误监控
npm install @sentry/nextjs
npx @sentry/wizard@latest -i nextjs
// sentry.client.config.ts
import * as Sentry from '@sentry/nextjs';
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
tracesSampleRate: 0.1,
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
enabled: process.env.NODE_ENV === 'production',
});
3. Core Web Vitals 上报
// components/WebVitals.tsx
'use client';
import { useReportWebVitals } from 'next/web-vitals';
import type { Metric } from 'web-vitals';
export function WebVitals() {
useReportWebVitals((metric: Metric) => {
fetch('/api/vitals', {
method: 'POST',
body: JSON.stringify({
name: metric.name,
value: metric.value,
id: metric.id,
label: metric.label,
}),
headers: { 'Content-Type': 'application/json' },
});
});
return null;
}
八、回滚策略
1. Vercel 一键回滚
Vercel 保留所有历史部署记录,可在控制台随时选择版本重新激活。
2. Git Tag 版本管理
# 发布时打标签
git tag -a v1.2.3 -m "Release version 1.2.3"
git push origin v1.2.3
# 回滚到上一版本
git checkout v1.2.2
npm run build
pm2 reload my-nextjs-app
3. 蓝绿部署(高可用场景)
生产流量 → Nginx → 蓝色环境(当前 v1.2.2)
→ 绿色环境(待发布 v1.2.3)
发布流程:
1. 在绿色环境部署新版本
2. 运行自动化测试
3. 切换 Nginx 流量至绿色环境
4. 蓝色环境保留作为回滚备份
九、部署前检查清单
每次生产部署前逐项确认:
- 所有单元测试通过
- TypeScript 类型检查通过
- ESLint 无错误
- 生产环境变量已配置
- 数据库迁移已执行(如有)
- .env.local 未提交到 Git
- 敏感 API Key 未出现在代码中
- next build 本地构建成功
- 健康检查接口可访问(/api/health)
- CDN 缓存策略已设置
- Sentry 错误监控已配置
- 回滚方案已确认
- 团队成员已通知
十、方案对比总结
| 方案 | 适用场景 | 复杂度 | 月成本 | 维护成本 |
|---|---|---|---|---|
| Vercel | 个人项目、初创公司、快速上线 | ⭐ | $0-20 | 极低 |
| Docker + 云服务器 | 企业级应用、合规要求 | ⭐⭐⭐ | $10-100 | 中等 |
| Node.js 自托管 | 已有服务器、运维团队 | ⭐⭐ | 固定成本 | 较高 |
无论选择哪种方案,CI/CD 流水线都是必须建立的——手动部署是事故的温床。从一开始就将质量检查和自动化部署内化到开发流程中,会让项目更健康、更稳定。