切换语言
切换主题

逃离 Vercel:Next.js Docker 自托管完全指南

上个月底,我照例打开 Vercel 的账单页面。$47.32。

盯着这个数字看了三秒,脑子里快速计算:这个月博客流量也就多了 20%,怎么账单直接翻倍了?点开详细账单,才发现问题出在 serverless function 的调用次数上——一个 API 路由因为没做好缓存,每次页面刷新都要跑三次。

说实话,Vercel 的开发体验确实丝滑:git push 就自动部署,边缘网络全球加速,还有各种开箱即用的功能。但当你的项目流量稍微上去一点,账单就像坐火箭。$20 的 Pro 套餐只是起步价,真正烧钱的是那些按量计费的部分。

那一瞬间我突然意识到:是时候把这个项目搬出去了。

这篇文章记录了我把 Next.js 项目从 Vercel 迁移到 Docker 自托管的完整过程。踩过的坑、查过的文档、试过的配置,我都整理在这里了。如果你也在考虑自托管,或者已经尝试过但遇到了各种奇怪的问题(比如静态资源 404、流式渲染不工作),希望这篇能帮到你。

$35-50
Vercel 月成本
波动取决于流量
$12
自托管月成本
固定成本
$300-500
年节省成本
可跑多个项目
200MB
Docker 镜像大小
三阶段构建优化后
数据来源: 实战数据

为什么要逃离 Vercel?

先说清楚,我不是要黑 Vercel。对于很多场景,它依然是最优解——尤其是企业级项目、需要全球边缘网络、或者团队没有运维能力的情况。但对于个人项目和小团队来说,成本确实是个硬伤。

Vercel 的定价逻辑

免费套餐看起来挺慷慨:100GB 带宽、100 万次 Edge Requests。问题是这些额度一个稍微有点流量的项目就不够用。一旦升级到 Pro($20/month),你会发现这只是入场券:

  • Serverless Function 调用:超过 100 万次后按量计费
  • 边缘函数执行时间:超过 100 万 GB-s 后加钱
  • 图片优化:超过 5000 次后按次收费
  • 带宽:超过 1TB 后每 GB 计费

最坑的是,这些用量很难提前预估。一个没做好缓存的 API 路由、一个被爬虫狂爬的页面,账单就会飙升。

自托管能省多少钱?

我算了笔账。我的项目在 Vercel 上每月大概 $35-50,波动取决于流量。迁移到 DigitalOcean 的 $12/month 服务器后:

  • 服务器:$12/month(2核4GB,足够跑两三个 Next.js 应用)
  • Cloudflare CDN:免费(反正也在用)
  • 额外存储:$0(本地磁盘够用)

月省 $25-40,一年就是 $300-500。更重要的是,这个成本是固定的,不会因为流量突增而暴涨。

什么时候适合自托管?

不是所有人都该自托管。我觉得符合以下条件的可以考虑:

  • ✅ 已经有一定的 Linux/Docker 基础
  • ✅ 项目流量相对稳定,不需要全球边缘网络
  • ✅ 可以接受 5-10 分钟的手动部署流程
  • ✅ 预算敏感(个人项目、创业早期)

反过来,如果你是这些情况,还是老实用 Vercel:

  • ❌ 团队没有运维能力,也不想学
  • ❌ 流量波动巨大,需要自动扩缩容
  • ❌ 需要 Vercel 的 Analytics、Edge Config 等专有功能
  • ❌ 预算充足,开发效率更重要

想清楚再动手,别为了省钱把自己折腾得够呛。

Next.js Docker 部署核心配置

好,进入正题。Next.js 的 Docker 部署有三个核心配置点,搞定这三个,基本就不会出大问题。

1. Standalone 输出模式

这是最关键的一步。默认情况下,next build 会生成一堆文件,包括完整的 node_modules。放到 Docker 里体积巨大,启动也慢。

next.config.js 里加这一行:

/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'standalone',
}

module.exports = nextConfig

npm run build 后,你会看到 .next/standalone 目录。这个目录里有:

  • server.js:启动脚本
  • 精简过的 node_modules:只包含运行时需要的包
  • 应用代码

关键点:standalone 模式不会自动复制 public.next/static。这俩必须手动复制到 standalone 目录里,不然静态资源全 404。这个坑我踩了两天才发现。

2. 多阶段 Dockerfile

直接贴我在用的 Dockerfile,注释写得很清楚了:

# ============ 阶段 1: 依赖安装 ============
FROM node:20-alpine AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app

# 只复制依赖清单,利用 Docker 缓存
COPY package.json package-lock.json ./
RUN npm ci

# ============ 阶段 2: 构建应用 ============
FROM node:20-alpine AS builder
WORKDIR /app

# 复制依赖和源码
COPY --from=deps /app/node_modules ./node_modules
COPY . .

# 构建时的环境变量(如果需要)
ENV NEXT_TELEMETRY_DISABLED=1

# 构建
RUN npm run build

# ============ 阶段 3: 生产运行 ============
FROM node:20-alpine AS runner
WORKDIR /app

ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1

# 创建非 root 用户(安全实践)
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

# 复制 public 文件夹(静态资源)
COPY --from=builder /app/public ./public

# 复制 standalone 输出
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
# 复制 static 文件(CSS/JS 等构建产物)
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"]

重点解释

  1. 三阶段构建:依赖安装、构建、运行分开,最终镜像只包含运行时需要的文件,体积能从 1.5GB 降到 200MB
  2. COPY --from=builder /app/public:别忘了这个,不然 favicon、robots.txt 都访问不到
  3. COPY ./.next/static:这个更关键,没有它所有 JS/CSS 都 404
  4. 非 root 用户:安全实践,生产环境别用 root 跑应用

3. 环境变量的坑

这个我也踩了。Next.js 的环境变量分两种:

  • 构建时变量:以 NEXT_PUBLIC_ 开头,会被编译进代码
  • 运行时变量:服务端用的,比如数据库地址

Standalone 模式下,runtimeConfig 不工作。官方推荐用 App Router 的方式:

// app/api/example/route.ts
export async function GET() {
  // 直接从 process.env 读取
  const dbUrl = process.env.DATABASE_URL
  // ...
}

Docker 运行时传环境变量:

docker run -p 3000:3000 \
  -e DATABASE_URL="postgres://..." \
  -e API_KEY="xxx" \
  your-image-name

或者用 docker-compose.yml:

version: '3.8'
services:
  nextjs:
    image: your-image-name
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: "postgres://..."
      API_KEY: "xxx"
    restart: unless-stopped

注意NEXT_PUBLIC_ 开头的变量必须在构建时就设置好,运行时改不了。如果需要运行时动态配置,只能用服务端环境变量。

反向代理配置要点

你可以直接把 Next.js 容器暴露到公网,但真别这么干。裸奔的 Node.js 应用面对各种恶意请求、慢速攻击,扛不住多久。反向代理不是可选项,是必需品。

为什么需要反向代理?

  1. 安全防护:拦截恶意请求、限流、防 DDoS
  2. HTTPS 支持:统一管理 SSL 证书
  3. 多应用部署:一台服务器跑多个项目,通过域名/路径区分
  4. 静态资源缓存:减轻应用服务器压力

我用的是 Nginx,稳定可靠。如果你想要更简单的配置,Caddy 也不错(自动 HTTPS,配置文件更人性化)。

Nginx 配置示例

server {
    listen 80;
    server_name yourdomain.com;
    
    # 强制跳转 HTTPS(如果配置了 SSL)
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name yourdomain.com;
    
    # SSL 证书配置(Let's Encrypt)
    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
    
    # 反向代理到 Next.js 容器
    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        
        # 必需的请求头
        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_buffering off;
        proxy_cache off;
        proxy_set_header X-Accel-Buffering no;
        
        # WebSocket 支持(如果需要)
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
    
    # 静态资源缓存(可选但推荐)
    location /_next/static/ {
        proxy_pass http://localhost:3000;
        proxy_cache_valid 200 60m;
        add_header Cache-Control "public, max-age=3600, immutable";
    }
}

三个关键配置

  1. proxy_buffering off:关闭缓冲,不然流式输出会卡住
  2. X-Accel-Buffering: no:明确告诉 Nginx 别缓冲响应体
  3. WebSocket 支持:如果你用了 Socket.io 或实时功能,Upgrade 头必须加

Caddy 的简化配置

如果觉得 Nginx 配置太繁琐,试试 Caddy:

yourdomain.com {
    reverse_proxy localhost:3000 {
        # Caddy 默认就不缓冲,不用特殊配置
    }
}

没了。Caddy 会自动申请和续期 Let’s Encrypt 证书,配置文件就这么简单。

Docker Compose 集成

把 Nginx 也容器化,管理更方便:

version: '3.8'
services:
  nextjs:
    build: .
    restart: unless-stopped
    environment:
      DATABASE_URL: "postgres://..."
    # 不暴露到宿主机,只让 nginx 访问
    expose:
      - "3000"
    networks:
      - app-network

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
      - ./certs:/etc/letsencrypt
    depends_on:
      - nextjs
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

注意 nextjs 服务用的是 expose 而不是 ports,这样只有同一网络内的容器能访问,更安全。

流式渲染失效问题解决

这个问题困扰了我整整一天。本地开发完全正常的 AI 聊天功能,部署到 Docker 后,流式输出直接失效——要么等半天一次性出来,要么直接卡住。

问题表现

典型的症状:

  • OpenAI/Anthropic API 的流式响应不工作
  • Server-Sent Events (SSE) 不实时推送
  • 页面要等很久才突然刷新,没有逐字输出的效果

本地 npm run dev 完全正常,一到生产环境就出问题。

根本原因

两个地方会导致这个问题:

  1. 反向代理缓冲:Nginx 默认会缓冲响应体,等完整内容到达才发送给客户端
  2. Next.js Runtime:非 Edge Runtime 的 API 路由在某些情况下不支持流式输出

解决方案 1: Nginx 配置

前面反向代理章节提到的三行代码,再强调一次:

proxy_buffering off;
proxy_cache off;
proxy_set_header X-Accel-Buffering no;

这三行必须加到 location / 配置块里。改完后重启 Nginx:

nginx -t  # 测试配置语法
nginx -s reload  # 重载配置

解决方案 2: 使用 Edge Runtime

如果你的 API 路由是用来做流式输出的(比如 AI 聊天),在文件顶部加这一行:

// app/api/chat/route.ts
export const runtime = 'edge'

export async function POST(req: Request) {
  const stream = new ReadableStream({
    async start(controller) {
      // 你的流式逻辑
      const response = await openai.chat.completions.create({
        model: 'gpt-4',
        messages: [...],
        stream: true,
      })

      for await (const chunk of response) {
        controller.enqueue(chunk.choices[0]?.delta?.content || '')
      }
      
      controller.close()
    },
  })

  return new Response(stream, {
    headers: {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
      'Connection': 'keep-alive',
    },
  })
}

Edge Runtime 是轻量级运行时,专门为流式响应优化过,在 Docker 环境下表现更稳定。

验证是否修复

用 curl 测试,能看到逐行输出就说明成功了:

curl -N http://yourdomain.com/api/chat \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"message": "Hello"}'

-N 参数关闭缓冲,你应该能看到内容一点点出来,而不是等很久突然全出来。

还是不行?检查这些

  1. Cloudflare 代理:如果用了 CF 的橙色云朵,它也会缓冲响应。要么关掉(灰色云朵),要么升级到 Pro 套餐(支持 Streaming)
  2. Docker 健康检查:某些健康检查配置可能干扰流式连接,检查 docker-compose.yml 里的 healthcheck 配置
  3. 负载均衡器:如果前面还有 Load Balancer,它也可能缓冲响应,需要单独配置

常见问题诊断与修复

整理了几个我踩过的坑和群友经常问的问题,基本涵盖了 80% 的部署失败场景。

问题 1: 静态资源 404

症状:页面能打开,但样式全乱,控制台一堆 404 错误,路径都是 /_next/static/...

原因:Dockerfile 里没有正确复制 .next/static 文件夹。

修复:检查你的 Dockerfile,确保有这两行:

COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public

如果已经有了还是 404,检查文件权限:

COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

问题 2: Docker 构建失败

症状docker build 时报错 “Could not find a production build in the ‘.next’ directory”

原因.dockerignore 配置不当,或者构建顺序有问题。

修复:创建 .dockerignore 文件,排除不必要的目录:

.next
node_modules
.git
.env*.local
out
.DS_Store
*.log

注意:.next 要排除,因为我们是在 Docker 容器里重新构建的。

问题 3: 环境变量不生效

症状:代码里读取 process.env.DATABASE_URL 返回 undefined

原因:环境变量传递方式不对,或者构建时 vs 运行时搞混了。

修复

  1. 运行时变量(数据库地址、API 密钥等),用 docker run -edocker-compose.yml 传递:

    docker run -e DATABASE_URL="..." your-image
  2. 构建时变量NEXT_PUBLIC_ 开头的),必须在 docker build 时传递:

    docker build --build-arg NEXT_PUBLIC_API_URL="https://api.example.com" .

    Dockerfile 里要声明:

    ARG NEXT_PUBLIC_API_URL
    ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL

问题 4: 内存不足导致构建失败

症状:构建到一半卡住或报错 “JavaScript heap out of memory”

原因:Node.js 默认内存限制不够,Next.js 大项目构建很吃内存。

修复:在 Dockerfile 的构建阶段增加内存:

# 在 builder 阶段
ENV NODE_OPTIONS="--max-old-space-size=4096"
RUN npm run build

或者用 Docker BuildKit 限制资源:

docker build --memory=8g --memory-swap=8g -t your-image .

问题 5: 容器启动后访问不了

症状:容器运行正常,但访问 http://localhost:3000 连接被拒绝。

原因:Next.js 默认监听 127.0.0.1,在 Docker 容器内部无法被外部访问。

修复:在 Dockerfile 里设置:

ENV HOSTNAME="0.0.0.0"
ENV PORT=3000

或者启动时传递:

docker run -p 3000:3000 -e HOSTNAME="0.0.0.0" your-image

快速诊断命令

遇到问题先跑这几个命令排查:

# 1. 检查容器是否在运行
docker ps

# 2. 查看容器日志
docker logs <container-id>

# 3. 进入容器查看文件结构
docker exec -it <container-id> sh
ls -la .next/
ls -la public/

# 4. 测试容器内部服务是否正常
docker exec -it <container-id> wget -O- http://localhost:3000

# 5. 检查端口映射
docker port <container-id>

结论

从 Vercel 迁移到 Docker 自托管,说实话没有想象中那么可怕。前期配置确实要花点时间,但一旦跑通了,后续维护成本很低。我现在的状态是:每月固定 $12 服务器费用,跑着三个 Next.js 项目,完全不用担心账单暴涨。

这篇文章的三个核心配置再总结一下:

  1. Standalone 模式 - next.config.js 加一行,记得手动复制 public.next/static
  2. 多阶段 Dockerfile - 三阶段构建,最终镜像 200MB 左右,启动快
  3. 反向代理 - Nginx 必须关闭缓冲(proxy_buffering off),不然流式渲染废掉

如果你卡在流式渲染问题上,99% 的情况是反向代理缓冲导致的,加上 export const runtime = 'edge' 基本能解决。

Vercel vs 自托管对比

维度VercelDocker 自托管
部署速度⚡️ git push 即部署🐢 5-10分钟手动操作
开发体验🌟 预览环境、日志、Analytics🔧 需要自己配置监控
成本💸 $20+/月,流量大了更贵💰 $12/月固定(可跑多个项目)
扩展性📈 自动扩缩容📊 手动调整资源
控制权⚠️ 受限于平台规则✅ 完全掌控
适用场景企业项目、全球服务个人项目、小团队、预算有限

最后的建议

  • 如果你是个人开发者,有多个 side project,自托管能省不少钱
  • 如果团队没有运维能力,或者项目流量波动很大,老实用 Vercel
  • 技术选型没有对错,只有适不适合

完整的配置文件和更多细节,我放在了 GitHub 仓库(仓库占位,实际使用时替换)。有问题欢迎留言讨论,踩过的坑就别让后面的人再踩了。

Next.js Docker 自托管完整部署流程

从配置 standalone 模式到部署上线的完整步骤,包含反向代理和流式渲染修复

⏱️ 预计耗时: 2 小时

  1. 1

    步骤1: 配置 Standalone 输出模式

    在 next.config.js 中启用 standalone 模式:

    1. 打开 next.config.js 文件
    2. 添加配置:output: 'standalone'
    3. 运行构建:npm run build
    4. 检查输出:确认 .next/standalone 目录已生成

    关键点:
    • standalone 模式不会自动复制 public 和 .next/static
    • 这两个目录必须在 Dockerfile 中手动复制
    • 否则静态资源会全部 404

    配置示例:
    ```javascript
    const nextConfig = {
    output: 'standalone',
    }
    module.exports = nextConfig
    ```
  2. 2

    步骤2: 创建多阶段 Dockerfile

    编写三阶段构建的 Dockerfile:

    阶段1 - 依赖安装:
    • 使用 node:20-alpine 作为基础镜像
    • 只复制 package.json 和 package-lock.json
    • 运行 npm ci 安装依赖(利用 Docker 缓存)

    阶段2 - 构建应用:
    • 从阶段1复制 node_modules
    • 复制所有源码
    • 运行 npm run build 构建应用

    阶段3 - 生产运行:
    • 创建非 root 用户(安全实践)
    • 复制 public 文件夹(静态资源)
    • 复制 .next/standalone 输出
    • 复制 .next/static 文件(CSS/JS 构建产物)
    • 设置 HOSTNAME="0.0.0.0" 和 PORT=3000
    • 启动命令:node server.js

    关键点:
    • 三阶段构建可将镜像从 1.5GB 降到 200MB
    • 必须复制 public 和 .next/static,否则静态资源 404
    • 使用非 root 用户运行,提升安全性
  3. 3

    步骤3: 配置 Nginx 反向代理

    设置 Nginx 反向代理并关闭缓冲:

    1. 安装 Nginx(或使用 Caddy)
    2. 配置 SSL 证书(Let's Encrypt)
    3. 创建 Nginx 配置文件

    关键配置(必须添加):
    • proxy_buffering off;(关闭缓冲)
    • proxy_cache off;(关闭缓存)
    • proxy_set_header X-Accel-Buffering no;(明确告诉 Nginx 不缓冲)

    必需请求头:
    • 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;

    WebSocket 支持(如需要):
    • proxy_set_header Upgrade $http_upgrade;
    • proxy_set_header Connection "upgrade";

    测试配置:
    ```bash
    nginx -t # 测试语法
    nginx -s reload # 重载配置
    ```

    注意:如果不关闭缓冲,流式渲染会失效
  4. 4

    步骤4: 处理环境变量

    区分构建时和运行时环境变量:

    构建时变量(NEXT_PUBLIC_ 开头):
    • 必须在 docker build 时传递
    • 使用 --build-arg 参数
    • Dockerfile 中声明:ARG NEXT_PUBLIC_API_URL
    • 设置环境变量:ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL

    运行时变量(服务端使用):
    • 使用 docker run -e 传递
    • 或在 docker-compose.yml 中配置
    • 代码中直接从 process.env 读取

    Standalone 模式下:
    • runtimeConfig 不工作
    • 必须使用 App Router 方式读取环境变量
    • 服务端代码:const dbUrl = process.env.DATABASE_URL

    示例:
    ```bash
    # 构建时
    docker build --build-arg NEXT_PUBLIC_API_URL="https://api.example.com" .

    # 运行时
    docker run -e DATABASE_URL="postgres://..." your-image
    ```
  5. 5

    步骤5: 修复流式渲染问题

    解决流式渲染失效(AI 聊天、SSE 等):

    问题表现:
    • 流式输出不工作,等很久才一次性出来
    • Server-Sent Events 不实时推送

    解决方案1 - Nginx 配置(必须):
    • 确保已添加 proxy_buffering off
    • 确保已添加 X-Accel-Buffering: no
    • 重启 Nginx 服务

    解决方案2 - 使用 Edge Runtime:
    • 在 API 路由文件顶部添加:export const runtime = 'edge'
    • Edge Runtime 专门为流式响应优化
    • 在 Docker 环境下表现更稳定

    验证修复:
    ```bash
    curl -N http://yourdomain.com/api/chat \
    -X POST \
    -H "Content-Type: application/json" \
    -d '{"message": "Hello"}'
    ```
    使用 -N 参数关闭缓冲,应该能看到逐行输出

    其他检查:
    • Cloudflare 代理:如果用了橙色云朵,也会缓冲(需关闭或升级 Pro)
    • Docker 健康检查:可能干扰流式连接
    • 负载均衡器:如果前面还有 LB,需要单独配置
  6. 6

    步骤6: 部署和验证

    构建镜像并部署:

    1. 构建 Docker 镜像:
    ```bash
    docker build -t nextjs-app .
    ```

    2. 运行容器:
    ```bash
    docker run -d \
    -p 3000:3000 \
    -e DATABASE_URL="postgres://..." \
    -e API_KEY="xxx" \
    --name nextjs-app \
    nextjs-app
    ```

    3. 验证部署:
    • 检查容器状态:docker ps
    • 查看日志:docker logs nextjs-app
    • 测试访问:curl http://localhost:3000
    • 检查静态资源:访问 /_next/static/ 路径

    4. 配置 Nginx 并重启:
    • 确保反向代理配置正确
    • 测试 HTTPS 访问
    • 验证流式渲染功能

    5. 监控和维护:
    • 设置容器自动重启:--restart unless-stopped
    • 定期查看日志排查问题
    • 监控服务器资源使用情况

    常见问题排查:
    • 静态资源 404:检查 Dockerfile 是否复制了 public 和 .next/static
    • 环境变量不生效:区分构建时和运行时变量
    • 容器无法访问:检查 HOSTNAME 是否设为 0.0.0.0

常见问题

自托管能省多少钱?成本对比如何?
Vercel 每月成本 $35-50(波动取决于流量),Docker 自托管固定 $12/月(可跑多个项目)。月省 $25-40,一年可省 $300-500。更重要的是自托管成本固定,不会因流量突增而暴涨。适合个人项目、小团队、预算敏感的场景。
为什么静态资源会 404?怎么修复?
standalone 模式不会自动复制 public 和 .next/static 目录。修复方法:在 Dockerfile 的 runner 阶段添加两行:
• COPY --from=builder /app/public ./public
• COPY --from=builder /app/.next/static ./.next/static
如果还是 404,检查文件权限,使用 --chown=nextjs:nodejs 设置正确的所有者。
流式渲染不工作怎么办?
99% 的情况是反向代理缓冲导致的。修复方法:
1) Nginx 配置必须添加:proxy_buffering off; proxy_set_header X-Accel-Buffering no;
2) API 路由使用 Edge Runtime:export const runtime = 'edge'
3) 检查 Cloudflare 代理(如果使用):关闭橙色云朵或升级 Pro 套餐
4) 验证:使用 curl -N 测试,应该能看到逐行输出
环境变量不生效怎么办?
区分构建时和运行时变量:
• NEXT_PUBLIC_ 开头:必须在 docker build 时用 --build-arg 传递,Dockerfile 中声明 ARG 和 ENV
• 运行时变量:用 docker run -e 或 docker-compose.yml 传递,代码中从 process.env 读取
• Standalone 模式下 runtimeConfig 不工作,必须使用 App Router 方式读取环境变量
Docker 镜像太大怎么办?
使用多阶段构建优化:
• 阶段1:只安装依赖(利用 Docker 缓存)
• 阶段2:构建应用
• 阶段3:只复制运行时需要的文件(standalone 输出、public、static)
• 最终镜像可从 1.5GB 降到 200MB
• 使用 node:20-alpine 基础镜像进一步减小体积
什么时候适合自托管?什么时候用 Vercel?
适合自托管:有 Linux/Docker 基础、流量稳定、预算敏感、可接受手动部署、个人项目/小团队。
适合 Vercel:团队无运维能力、流量波动大需要自动扩缩容、需要全球边缘网络、需要 Vercel 专有功能(Analytics、Edge Config)、预算充足更看重开发效率。
容器启动后无法访问怎么办?
检查以下几点:
1) HOSTNAME 必须设为 0.0.0.0(不能是 127.0.0.1),在 Dockerfile 中设置 ENV HOSTNAME="0.0.0.0"
2) 端口映射是否正确:docker run -p 3000:3000
3) 容器是否在运行:docker ps
4) 查看容器日志:docker logs <container-id>
5) 测试容器内部:docker exec -it <container-id> wget -O- http://localhost:3000

12 分钟阅读 · 发布于: 2025年12月20日 · 修改于: 2026年1月22日

评论

使用 GitHub 账号登录后即可评论

相关文章