封面

ByteGrad · 29:50 · 发布 2026-06-12 · 3509次观看(截至抓取) · 观看原视频

主旨

视频演示把一个 Next.js 应用从零容器化并部署到 Hostinger VPS 的完整链路:Next.js standalone 输出 → 写或复用 Dockerfile → 本地烟测 → 多架构镜像推送到 GHCR → 在 VPS 上拉取并以 Docker 容器运行 → 用 Caddy 做反向代理接管 HTTPS → 自定义域名解析 → Cloudflare CDN 接管静态资源。

核心论点

  1. 使用 Next.js 的 output: "standalone" 是 Docker 化的关键前置:它让构建产物自带一个最小化 Node.js server.js,服务器侧特性(路由处理器/server actions/中间件)都可以继续跑。(→ 详解1)
  2. AGENTS.md 适合写 agent 看不见的部署上下文(Docker/Caddy/CDN 这一类不在项目文件里的运维决定),让编码 agent 不会被错误信息误导去改源码。(→ 详解2)
  3. Dockerfile 既可以让 AI 起草,也可以直接复用官方模板,后者版本更新更及时:Next.js docs 里 docker-standalone-output 模板自带 Node 长期支持版本、多阶段构建、缓存复用,适合先照搬。(→ 详解3)
  4. 跨平台构建必须显式指定 --platform:本机与 VPS 架构常常不同(本片里本机是 MacBook,VPS 通常是 Linux),不指定平台镜像会被 VPS 直接拒绝。(→ 详解4)
  5. GHCR(ghcr.io)是 Docker 镜像的零成本托管:用 GitHub Personal Access Token 登录后即可 docker push。(→ 详解5)
  6. VPS 上跑容器时务必加 --restart unless-stopped:VPS 自身可能重启或更新,容器不挂这个标志会一直处于停机状态。(→ 详解6)
  7. Caddy 是”开箱即 HTTPS”的反向代理:只要给它一个域名,自动申请并续签 SSL 证书并 80→443 重定向,不需要额外配置证书或手动续签。(→ 详解7)
  8. Cloudflare 免费档即可提供全球 CDN + 缓存命中可视化:只要把域名的 name server 切到 Cloudflare,JS/CSS 等静态资源自动走边缘节点,响应头里能看到 cf-cache-status: HIT/MISS。(→ 详解8)

知识点详解

1. Intro 与 Next.js 的 output: standalone 00:00

视频开头明确说”很久没讲 Docker 化 Next.js 并部署到 VPS 的主题了,而这一次可以比以前的视频做得更简单”。Next.js 部署文档列出了 Node.js 服务器与 Docker 容器两种主路径,两种都支持 server actions、route handlers、proxy、middleware 这类服务器侧特性;另有一种 static export 路径会让产物只剩纯 HTML,丢掉所有服务器侧能力。

Next.js 提供两类 Docker 模板:docker-standalone-output 与多环境模板。本片选 standalone 路线,因为它构建完会直接产出 server.js 这个最小化 Node 服务器,运行时只需 node server.js。具体前置动作是在 next.config.js 里写 output: "standalone",这样 npm run build 之后 .next/standalone/ 下就会带可独立运行的 server.js。这是把 Next.js 应用从托管平台挪到自有 VPS 的最干净入口。

output: standaloneoutput: export 的关键差别是:前者保留 Node 服务器与所有运行时特性,后者只生成静态 HTML。视频明确说”如果用了 static export,server actions/route handlers 这类都用不了”,所以选了 standalone。

2. AGENTS.md 写部署上下文 03:52

Wesley 提示:AI 编码 agent 只看得见项目目录里的文件,看不见外部部署上下文。Docker 用什么、镜像推到哪、反代用什么、CDN 在不在前面——这些信息不在源码里,agent 默认不知道。如果你想让 agent 帮你维护或扩展 Dockerfile/Caddyfile 等运维文件,把这些决定写进 AGENTS.md。

具体写法是写下”这里用 Docker 部署、镜像推到 GHCR、用 Caddy 做反代、Cloudflare CDN 在前面”这类句子,再补一句”不要做”清单(例如不要改某固定配置的容器)。如果暂时不需要 agent 介入,这个文件可以删掉;本片为了演示默认 Dockerfile 的版本,就把 AGENTS.md 移除了。

这条经验把 AGENTS.md 从 Claude Code 专有配置文件提升为”任何 agent 都需要看到的运行时/运维上下文”的载体——它原本被作为”每次会话开头自动注入”的系统级规范,这里把它推广到部署运维上下文。

3. Dockerfile 生成:AI 起草 vs Next.js 官方模板 04:58

生成 Dockerfile 有两条路。第一条是让 AI 编码 agent 起草——好处是它会自动读 AGENTS.md 并指向 Next.js 文档,知道 standalone 输出需要把 .next/staticpublic/ 手动复制到镜像里。一两分钟就能出一份多阶段 Dockerfile 与 .dockerignore,后者会顺手忽略 .env(避免把密钥烤进镜像)等敏感文件。

但 AI 起草容易把 node 版本钉死,例如旧版本镜像;第二条路是直接复用 Next.js 官方 docker-standalone-output 模板的 Dockerfile。视频里 Wesley 比对后说官方模板用的是更新版本的 Node 长期支持版本,这点比 AI 起草更跟得上节奏。模板还会处理多阶段构建(先在带 dev deps 的镜像里装依赖、跑 build,再切到最小运行时镜像只留产物体积)、cache mount 加速后续构建,以及自动按 package-lock.json/yarn.lock/pnpm-lock.yaml 判定包管理器。

实操上视频推荐:先照搬官方模板,等跑通后再让 agent 优化。这也呼应了 Skill 反推创建法 的精神——手动跑通一遍再让 AI 总结规律,而不是让 AI 直接给出”应该长这样”的 Dockerfile。

4. Docker 构建:本地烟测与目标平台 07:51

先在本地 docker build + docker run 跑一遍确认能用,再考虑推到生产。这是视频明确强调的顺序:Mac 上用 Docker Desktop 构建并运行容器,把端口映射到 localhost:3000,访问页面、点 server actions、试导航——三项都正常才能进入下一步。

本地烟测的副作用是 Docker Desktop 镜像体积很大(包含整个 Node 运行时与所有依赖),跑完应当先 docker rm 容器再 docker rmi 镜像,把空间腾出来。否则一台机器很快被几十个废弃镜像塞满。

给生产 VPS 构建时必须显式指定 --platform。视频里 Wesley 提到本机是 MacBook、VPS 是 Linux 系统且通常是 AMD64;不指定平台,镜像会被 VPS 的 Docker daemon 直接拒绝。具体命令是 docker build --platform linux/amd64 -t ghcr.io/<user>/<repo>:latest .

5. 推镜像到 GitHub Container Registry (ghcr.io) 10:38

VPS 不会自己构建镜像——它需要一个能 docker pull 的镜像源。视频全程用 GHCR (GitHub Container Registry),域名为 ghcr.io,免费且与 GitHub 账号天然打通。推送前先用 docker login ghcr.io 登录,密码不是 GitHub 密码而是 Personal Access Token (PAT):在 GitHub Settings → Developer settings → Personal access tokens → Tokens (classic) 生成,权限勾选与 packages 相关的 read/write 即可。

Wesley 建议设短过期(他本人几个月就轮换一次),并把 token 当作密码一样保护——它能推镜像进你的账号空间。登录成功后 docker push ghcr.io/<user>/<repo>:latest 把镜像推上去,GitHub 用户页面的 Packages tab 立即可见。

PAT + short expiry 是这一套流程里唯一的”凭据管理”环节。视频没展开讨论 secret rotation,但实操层面相当于把 GitHub 密码的子集授权给一台开发机,泄露面比直接交密码更小。

6. VPS 初始化、SSH 与拉取运行镜像 12:13

采购环节,视频选用 Hostinger 的 KVM 2 套餐:2 vCPU / 8 GB RAM / 100 GB / 8 TB 月流量。视频提到 Hostinger 还提供 Docker Manager(可粘贴 docker-compose URL 自动构建),但他建议新手至少先手动跑一遍——等熟悉后,再切到 Docker Manager 这种托管方案。本片选纯 Ubuntu 系统镜像、勾选 malware scanner,采购流程里 server location 可选离目标用户最近的地域。

VPS 就绪后,SSH 进入服务器是第一步。视频使用 ssh root@<ip> 配合购买时设的 root 密码登录。生产环境应当建一个非 root 用户并给 sudo 权限,视频里为了简化演示直接以 root 继续——这是一个值得注意的简化,真实部署应单独处理。

登录后做三件事:apt update 把系统包升级到最新;② 验证 Docker 已预装(Hostinger Ubuntu 镜像自带,省去 apt install docker.io 步骤);③ docker run hello-world 跑一个烟测,确认 daemon 在线并能拉远端镜像。

接下来用同一份 PAT 登录 GHCR、docker pull 镜像。然后是关键命令:docker run -d --restart unless-stopped -p 3000:3000 ghcr.io/<user>/<repo>:latest--restart unless-stopped 是 VPS 场景下的标配:Ubuntu 可能因为系统更新或物理机重启自己重启,而这个标志让容器在 daemon 起来后自动跟上,不必每次手动 docker start

到这里,在浏览器输入 http://<VPS IP>:3000 就能看到 Next.js 应用,服务器组件、route handler、server action 全部正常。Wesley 也跑了一次 curl 验证返回 HTTP 200,并用 docker logs <container_id> 看到了熟悉的 Next.js 启动日志。但此时应用是裸奔在公网上——任何人都能通过 IP+端口访问,没有 HTTPS、没有域名、没有缓存。下一节用 Caddy 把这一切收口。

7. Caddy 反向代理 + 自定义域名 + HTTPS 19:51

裸跑 Next.js 容器有两个问题:① 端口 3000 暴露在公网,直接给攻击面;② 没有 HTTPS,所有流量明文。视频给出的方案是停掉容器、用 localhost:3000 在容器内重新跑,再在外面套一层 Caddy。Caddy 监听公网 80(HTTP)/443(HTTPS)端口,内部把请求反代到本机 3000 上的容器。

Caddy 的两个杀手特性:① 自动 HTTPS——只要给它一个域名,它自动申请并续签 SSL 证书并配置 80→443 重定向;“it is using HTTPS even. So, it’s automatically been able to do that as well”,用户无需手动管理证书续签;② 配置文件极简——Caddyfile 只写一行域名 + reverse_proxy localhost:3000 就完事,远比 Nginx 同等配置短。

域名接入步骤:① 在域名注册商加 A 记录,把根域名指到 VPS IP;nslookup 验证 DNS 已生效;③ 编辑 Caddy 配置文件,把 demo 域名换成自己的域名;④ 重新格式化 + 校验 + reload Caddy 服务。完成后 https://<your-domain>/ 直接显示 Next.js 应用,证书自动签发。

安全层面,Wesley 顺手演示了 systemctl status caddy 确认服务以 systemd 方式常驻,并提示应当用 Ubuntu 防火墙或 Hostinger 自带防火墙把 3000/其他不必要端口关掉,只留 80/443 + SSH。这与源站隔离(Origin Isolation) 的精神一致——让真实业务容器只在内网暴露,公网入口集中到反向代理。

8. Cloudflare CDN 接入 25:53

VPS 单点再快也有地理延迟。Cloudflare 免费档提供全球 CDN + 安全防护 + TLS 终结——而 Hostinger 自家的 CDN 仅限”website hosting”产品,VPS 用户必须走第三方,Cloudflare 是 Hostinger 官方文档里点名推荐的方案。

接入流程:① Cloudflare 后台 Add a site → 选 free plan → 它自动发现两条 DNS 记录并启用代理;② 把域名的 name server 从原注册商切到 Cloudflare 分配的两个;③ 等待 NS 生效,Cloudflare 自己说最长可能要几小时,实际上多数十几分钟到半小时就够了。视频里 Wesley 大约 5 分钟后回来刷新,状态已经是”my domain is now protected by Cloud Flare. Your web traffic is proxying through Cloud Flare”。

接入后CDN 缓存状态可以从浏览器 Network 面板直接验证:打开 DevTools → Network → 选中某个 JS 文件 → Response Headers,能看到 cf-cache-status: MISS(首次请求,Cloudflare 回源拉了一次)→ 刷新后再看 cf-cache-status: HIT(命中边缘节点)。更硬核的诊断是访问 https://<your-domain>/cdn-cgi/trace,Cloudflare 返回一段纯文本,展示本次请求命中的数据中心 colo code —— 证明流量确实经过 Cloudflare 边缘,而不是直连 VPS。

Cloudflare 的额外价值:与 Caddy 的自动 HTTPS 配合,Cloudflare 会用自家签发的证书(用户无需在源站再申请一次),Caddy 这边仍然处理源站到 Cloudflare 之间的 HTTPS 加密。这是一种很干净的”边缘终止 + 源站再加密”分层结构。

可执行步骤

  • next.config.js 里设 output: "standalone",跑一次 npm run build 确认 .next/standalone/server.js 生成。
  • 复制 Next.js 官方 docker-standalone-output 模板的 Dockerfile 与 .dockerignore 到项目根目录;手动调整 Node 版本号到当前长期支持版本。
  • 本地 docker build -t <name> . + docker run -p 3000:3000 <name>,访问 localhost:3000 验证 server actions / route handlers / 路由跳转全部正常,然后 docker rm + docker rmi 清理。
  • 在 GitHub 生成 Personal Access Token(勾选 packages 读写、短过期),本地 docker login ghcr.io 后用 docker build --platform linux/amd64 -t ghcr.io/<user>/<repo>:latest . 构建生产镜像并 docker push
  • 采购 Hostinger VPS(纯 Ubuntu 镜像),SSH 用 root 登录,apt update + docker run hello-world 验证 daemon;为安全起见建一个 sudo 用户并禁用 root SSH。
  • 在 VPS 上 docker login ghcr.iodocker pull ghcr.io/<user>/<repo>:latestdocker run -d --restart unless-stopped -p 127.0.0.1:3000:3000 ghcr.io/<user>/<repo>:latest,让容器只在内网可访问。
  • 在 VPS 上安装 Caddy,编辑它的配置文件把域名反向代理到 localhost:3000,systemctl reload caddy,让 Caddy 自动签发并续签 SSL 证书。
  • 在域名注册商加 A 记录把根域名指向 VPS IP,并把 name server 切到 Cloudflare 分配的 NS,等 NS 生效后在 Cloudflare 后台启用代理。
  • 用 Ubuntu 防火墙(或 Hostinger 自带防火墙)关闭 3000 等非必要端口,只保留 80/443/SSH;在浏览器 Network 面板验证 JS/CSS 请求带 cf-cache-status: HIT

关联

  • 2026-01-30-买Mac Mini前先用VPS自托管AI助手agent — 互补 — 本片补齐”前端 Web 应用”的部署路径,对方补”自托管 AI 助手 agent”在 VPS 上的部署路径,主机相同但服务形态完全不同。
  • 2026-04-25-在VPS上自托管Hermes Agent与Dashboard面板 — 互补 — 同上,本片讲带反向代理与 CDN 的 Web 应用部署,对方讲 Hermes Agent 与面板的部署,共享”同一台 VPS 可同时跑多服务”的论点。
  • VPS 自托管 — 进阶 — 沿用本片同样的 VPS 采购与基础运维流程;先读本片掌握 Caddy/Cloudflare/HTTPS 的标准壳层,再回头把任意服务(自托管 agent、自建应用)塞进同一台 VPS。

术语

  • Docker image vs Docker container:镜像是一份只读打包(代码+运行时+依赖),容器是镜像的运行实例。
  • Dockerfile:声明镜像构建步骤的纯文本文件,常用多阶段写法让最终镜像只留产物体积。
  • Standalone output:Next.js 的构建模式,使产物只带一个最小化 server.js,无需在镜像里复制整棵 node_modules。
  • GHCR (GitHub Container Registry):GitHub 提供的免费 Docker 镜像托管,域名 ghcr.io,与 GitHub PAT 鉴权。
  • Personal Access Token (PAT):GitHub 用于代替密码授权 API/Packages 的短过期凭据。
  • Reverse proxy:对外接收 HTTP/HTTPS 请求、内部按规则转发到本机服务的中间层,本片用 Caddy。
  • Caddy:现代 Web 服务器,以”自动 HTTPS”与极简配置文件著称。
  • Cloudflare CDN:把静态资源缓存到全球边缘节点的免费 CDN,接入方式是把域名的 NS 切到 Cloudflare。
  • --platform linux/amd64:跨架构 Docker 构建必备参数,让本机架构构建的镜像能被 VPS 接受。
  • --restart unless-stopped:Docker run 的重启策略,VPS 重启后容器自动跟上。
  • A 记录:把域名直接指向一个 IP 地址的 DNS 记录类型。
  • Nameserver (NS):负责告诉 DNS 查询该域名由哪台权威服务器解析,接入 Cloudflare 时需要切到 Cloudflare 分配的 NS。
  • cf-cache-status:Cloudflare 在响应头里返回的缓存命中状态,首次 MISS、刷新后变 HIT,证明请求确实走了 Cloudflare 边缘。

金句

“we can make it much simpler than what we did in some of the other videos.” — 把 Next.js Docker 化部署这件事的复杂度说清楚:与过去几期相比,本片的官方模板 + GHCR + Caddy 组合可以省掉手写反代配置与手动证书申请。(00:14)

“you may want to specify the platform for which the image is meant for.” — 跨架构构建时最容易被忽略的一步,缺失会导致 VPS daemon 直接拒绝拉取。(10:09)

“Caddy is a reverse proxy as it’s called. There are similar ones like Nginx.” — 把 Caddy 在整个栈里的角色一句话说清:外部入口 + 内部反代,Next.js 容器在 localhost:3000 上完全私有。(21:20)

“my domain is now protected by Cloud Flare. Your web traffic is proxying through Cloud Flare, meaning it should be faster with caching speed and traffic services.” — Cloudflare 启用代理后的官方提示:从这一刻起所有流量都过边缘节点,缓存与 SSL/TLS 由 Cloudflare 终结。(28:04)

立场与利益

视频全程使用 Hostinger 作为示例 VPS 提供商,作者在 description 里写明 Hostinger 是当日赞助方并提供 coupon code BYTEGRAD;对 Hostinger 自带 Docker Manager 等”托管化”方案做了正面介绍,但同时明确建议”先手动跑一遍,理解流程再切到托管”。Kinde 在 description 里被列为付费品牌大使,但本片内容完全不涉及 Kinde。

把关键主张分档:

  • 与利益同向(待印证):对 Hostinger VPS 与 Docker Manager 的整体推荐——选它确实方便,但 Hostinger 是否在所有 Next.js 场景下都是最优选择,与同价位其他家 VPS 提供商的对比未做,需要外部印证。
  • 利益中性:Caddy 反代、自动 HTTPS、--restart unless-stopped--platform 显式指定、Cloudflare CDN 接入——这些是与赞助方无关的通用工程经验,按内容本身采信。
  • 与利益反向(可信度最高):Wesley 在 Hostinger 自带 Docker Manager 段落里特意提醒”先手动跑一遍再切到托管方案”——这一句是反对”用脚手架图省事”的产品倾向,直接利好观众,与他作为博主希望简化流程的本能反向。

价值定位

  • 适合谁:已经在本地跑通 Next.js、希望把同一个应用放到自有 VPS 上公开访问的人;尤其是 solo builder / indie hacker 想绕开托管平台锁定、把应用和数据握在自己手里的场景。
  • 解决什么:给出一套”Next.js → Dockerfile → GHCR → VPS → Caddy → Cloudflare”的端到端最小可用栈,每一步都有可执行的命令和具体的坑位提示(平台架构、容器重启策略、端口暴露范围、Cloudflare NS 切换)。
  • 认知 vs 实操:两者兼备,但更偏实操——本片不是讲”为什么用 Docker 部署”,而是带观众一步步敲命令并验证每一步(本地 3000 端口可访问、VPS IP+3000 可访问、域名+HTTPS 可访问、CDN 边缘命中)。读完后可以直接照搬复制一份到自己的 VPS。
  • 2026-04-25-在VPS上自托管Hermes Agent与Dashboard面板 重叠:对方是 Hermes Agent 一键部署,本片是手动版 Web 应用部署。重叠部分是”主机是 Hostinger VPS”,独有增量是”Web 应用需要的 Caddy/HTTPS/Cloudflare 完整壳层”——对方没讲 Caddy 与 Cloudflare,本片没讲一键 agent 部署。

自检问题

  1. 视频推荐用 Next.js 的哪种构建模式作为 Docker 化的前置,以及为什么? 答案:在 next.config.js 里设 output: "standalone",这样 npm run build 会产出最小化 Node server.js,镜像里只需带产物体积即可保留 server actions/route handlers 等服务器侧特性。见「知识点详解1」00:00
  2. 在 MacBook 上构建给 Linux VPS 用的镜像,哪条命令参数必须显式加上,缺失会导致什么后果? 答案:--platform linux/amd64,因为本机与 VPS 架构不同(视频里本机 MacBook、VPS 通常是 Linux AMD64),缺失时 VPS 的 Docker daemon 会直接拒绝拉取镜像。见「知识点详解4」10:09
  3. 在 VPS 上跑 Next.js 容器时,为什么要加 --restart unless-stopped? 答案:VPS 自身可能被重启或做系统更新,这个标志让 Docker daemon 恢复时自动把容器也拉起,避免每次都要手工 docker start。见「知识点详解6」18:50
  4. Caddy 与 Nginx 相比,在 HTTPS 这一步上有什么简化? 答案:Caddy 只要在 Caddyfile 里写域名就自动申请并续签 SSL 证书并配置 80→443 重定向,不需要手动写证书路径或续签脚本。见「知识点详解7」22:32
  5. 把域名 NS 切到 Cloudflare 后,怎么确认 CDN 已经生效? 答案:打开浏览器 Network 面板,选一个 JS/CSS 请求看 Response Headers 里的 cf-cache-status,首次是 MISS,刷新后变 HIT;更直接的是访问 /cdn-cgi/trace 看返回的 colo code 是不是 Cloudflare 数据中心。见「知识点详解8」28:32