Hugo 博客双仓库部署方案:GitHub Pages + Vercel
文章目录
本文介绍如何用两个 GitHub 仓库实现 Hugo 博客的自动化构建和部署,同时接入 Vercel 作为免费 CDN 加速方案。
架构概览
graph TD
A["🔒 {user}.github.io.source<br/>私有仓库 · 源码"] -->|git push| B["⚙️ GitHub Actions<br/>Hugo Build + Pagefind"]
B -->|deploy key push| C["📦 {user}.github.io<br/>公开仓库 · 静态文件"]
C -->|内置部署| D["🌐 GitHub Pages"]
C -->|Webhook 触发| E["🚀 Vercel CDN"]
核心思路:
- 源码仓库(私有)负责写作和版本管理
- 静态文件仓库(公开)只存放 Hugo 构建产物
- GitHub Pages 和 Vercel 各自从静态文件仓库部署
一、源码仓库配置
1.1 创建仓库
创建私有仓库 {username}.github.io.source,存放 Hugo 项目源码:
├── archetypes/
├── content/
│ └── post/ # 博客文章
├── layouts/ # 自定义模板(覆盖主题)
├── static/ # 静态资源
├── themes/
│ └── even/ # 主题(git submodule)
├── config.toml # Hugo 配置
└── .github/
└── workflows/
└── gh-pages.yml # CI/CD 配置
1.2 GitHub Actions Workflow
.github/workflows/gh-pages.yml:
name: Deploy Hugo Site to Github Pages on Master Branch
on:
push:
branches:
- master
workflow_dispatch: # 支持手动触发
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
build-deploy:
runs-on: ubuntu-latest
steps:
# 1. 拉取源码(含 submodule 主题)
- uses: actions/checkout@v4
with:
submodules: true
fetch-depth: 0
# 2. 安装 Hugo
- name: Setup Hugo
uses: peaceiris/actions-hugo@v3
with:
hugo-version: '0.136.5'
extended: true
# 3. 构建静态文件
- name: Build
run: hugo --minify
# 4. 构建搜索索引(可选,使用 Pagefind)
- name: Build search index
run: npx pagefind --site public
# 5. 部署到静态文件仓库
- name: Deploy
uses: peaceiris/actions-gh-pages@v4
with:
deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }}
external_repository: {username}/{username}.github.io
publish_dir: ./public
keep_files: false
publish_branch: master
cname: www.example.com # 自定义域名
commit_message: ${{ github.event.head_commit.message }}
1.3 Workflow 原理
整个流程分 5 步:
Checkout:拉取源码,
submodules: true确保主题代码也被拉取,fetch-depth: 0获取完整 git 历史(用于.GitInfo和.Lastmod)Setup Hugo:下载指定版本的 Hugo Extended(Extended 版本支持 SCSS/SASS 编译)
Build:
hugo --minify将 Markdown 编译为 HTML,输出到./public目录,--minify压缩 HTML/CSS/JSSearch Index(可选):Pagefind 扫描
./public中的 HTML 文件,生成静态搜索索引到./public/pagefind/Deploy:使用
peaceiris/actions-gh-pages将./public目录的内容 push 到目标仓库。keep_files: false表示每次清空目标仓库再推送,确保内容一致
1.4 Deploy Key 配置
源码仓库通过 SSH Deploy Key 向静态文件仓库推送,配置步骤:
# 生成密钥对
ssh-keygen -t ed25519 -C "github-actions-deploy" -f deploy_key -N ""
- 公钥
deploy_key.pub→ 添加到{username}.github.io仓库:Settings → Deploy keys → Add deploy key → 勾选 “Allow write access” - 私钥
deploy_key→ 添加到{username}.github.io.source仓库:Settings → Secrets and variables → Actions → New repository secret,名称为ACTIONS_DEPLOY_KEY
⚠️ 重要限制:Deploy Key push 的 commit 不会触发目标仓库的 GitHub Actions workflow(GitHub 安全机制,防止循环触发)。但 GitHub Pages 的内置
pages-build-deployment和 Vercel 的 Webhook 不受此限制。
1.5 Mermaid 图表支持
Hugo 不内置 Mermaid 支持,但通过代码块渲染钩子可以轻松实现,且不需要修改主题代码。
Hugo 的模板优先级是 项目 layouts/ > 主题 layouts/,只需在项目根目录添加文件即可。
只需两个文件:
文件一:代码块渲染钩子
layouts/_default/_markup/render-codeblock-mermaid.html:
<pre class="mermaid">
{{ "{{" }} .Inner | htmlEscape | safeHTML {{ "}}" }}
</pre>
{{ "{{" }} .Page.Store.Set "hasMermaid" true {{ "}}" }}
- 将 mermaid 代码块输出为
<pre class="mermaid">标签 - 标记当前页面包含 Mermaid 内容
文件二:覆盖 baseof.html
将主题的 baseof.html 复制到 layouts/_default/baseof.html,在 </body> 前添加:
{{ "{{" }} if .Store.Get "hasMermaid" {{ "}}" }}
<script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.esm.min.mjs';
mermaid.initialize({ startOnLoad: true });
</script>
{{ "{{" }} end {{ "}}" }}
只在包含 Mermaid 代码块的页面才加载 JS,按需加载不影响其他页面性能。语法与 Obsidian 完全一致,两边可以无缝复制。
二、静态文件仓库配置
2.1 创建仓库
创建公开仓库 {username}.github.io。这是一个特殊的仓库名,GitHub 会自动将其识别为 GitHub Pages 站点。
这个仓库不需要手动维护,所有内容由源码仓库的 workflow 自动推送。
2.2 GitHub Pages 配置
- 进入仓库 Settings → Pages
- Build and deployment → Source → 选择 “Deploy from a branch”
- Branch 选
master,目录选/ (root) - 保存
选择 “Deploy from a branch” 而不是 “GitHub Actions” 的原因:
- Deploy Key push 的 commit 不会触发自定义 workflow
- 但 “Deploy from a branch” 使用的是 GitHub 内置的
pages-build-deployment,不受此限制 - 每次源码仓库 push 新内容过来,GitHub Pages 会自动部署
2.3 自定义域名
如果需要自定义域名:
- 在 DNS 服务商添加 CNAME 记录:
www.example.com→{username}.github.io - workflow 中的
cname: www.example.com会自动在仓库根目录创建CNAME文件 - 在 GitHub Pages 设置中勾选 “Enforce HTTPS”
三、Vercel 免费部署方案
3.1 为什么用 Vercel
- 全球 CDN 加速,国内访问速度优于 GitHub Pages
- 免费 HTTPS
- 自动部署,零配置
- 支持自定义域名
- Hobby 计划完全免费
3.2 接入步骤
- 注册 Vercel,使用 GitHub 账号登录
- Import Project → 选择
{username}.github.io仓库 - Framework Preset 选择 Other(因为这是已经构建好的静态文件,不需要 Vercel 再构建)
- Build Command 留空或设为
echo "skip build" - Output Directory 设为
.(根目录) - 点击 Deploy
3.3 Deployment 原理
sequenceDiagram
participant R as GitHub Repo
participant W as Vercel Webhook
participant V as Vercel Build
participant C as Vercel CDN
R->>W: push 事件(HTTP POST)
W->>V: 触发构建
V->>R: 拉取最新代码
V->>V: 构建阶段(静态文件跳过)
V->>C: 分发到全球边缘节点
C-->>C: 用户就近访问
Webhook 监听:Vercel 在你导入项目时,会在 GitHub 仓库自动注册一个 Webhook。每次仓库收到 push 事件,GitHub 会向 Vercel 发送 HTTP POST 通知
拉取代码:Vercel 收到通知后,从 GitHub 拉取最新代码
构建阶段:由于静态文件仓库已经是构建好的 HTML/CSS/JS,不需要额外构建。Vercel 会检测到没有框架配置,直接将文件作为静态资源处理
部署到 CDN:Vercel 将静态文件分发到全球边缘节点(Edge Network),用户访问时自动路由到最近的节点
Deployment 类型:
- Production Deployment:master 分支的 push 会触发生产环境部署,绑定到自定义域名
- Preview Deployment:其他分支的 push 会生成预览 URL(本方案中不涉及,因为静态文件仓库只有 master 分支)
3.4 Vercel 自定义域名
- Vercel Dashboard → 项目 → Settings → Domains
- 添加自定义域名,如
www.example.com - 按提示在 DNS 服务商添加 CNAME 记录:
www.example.com→cname.vercel-dns.com - Vercel 自动签发 SSL 证书
💡 可以让 GitHub Pages 和 Vercel 使用不同的域名,比如
{username}.github.io走 GitHub Pages,www.example.com走 Vercel。
3.5 Node.js 版本配置
Vercel 构建时需要 Node.js 环境。如果遇到版本问题:
- 方式一:Vercel Dashboard → 项目 → Settings → Build and Deployment → Node.js Version → 选择 24.x
- 方式二:在仓库根目录添加
package.json:
{
"engines": {
"node": "24.x"
}
}
四、完整部署流程
sequenceDiagram
participant D as 开发者
participant S as 源码仓库
participant A as GitHub Actions
participant T as 静态文件仓库
participant GP as GitHub Pages
participant VC as Vercel CDN
D->>S: 1. git push(写文章)
S->>A: 2. 触发 workflow
A->>A: 2a. Hugo 构建 Markdown → HTML
A->>A: 2b. Pagefind 生成搜索索引
A->>T: 2c. Deploy Key push 静态文件
T->>GP: 3a. 内置部署自动触发
T->>VC: 3b. Webhook 自动触发
GP-->>GP: 部署完成
VC-->>VC: 部署到全球 CDN
整个过程从 push 到上线通常在 1 分钟内完成。
五、注意事项
Deploy Key vs PAT:Deploy Key 只能访问单个仓库,更安全;PAT 可以访问所有仓库,但 PAT push 的 commit 能触发目标仓库的 workflow。根据需求选择
主题作为 Submodule:使用
git submodule add引入主题,workflow 中submodules: true确保主题被正确拉取。升级主题只需git submodule update --remote模板覆盖:Hugo 的模板优先级是
项目 layouts/ > 主题 layouts/,自定义模板放在项目的layouts/下即可,不需要修改主题代码keep_files: false:每次部署会清空目标仓库再推送。如果在静态文件仓库手动添加了文件(如CNAME),需要通过 workflow 配置(如cname参数)来保留构建缓存:如果构建产物没有变化,
peaceiris/actions-gh-pages会跳过 push(nothing to commit),不会产生多余的 commit