Featured image of post Hugo 博客自动部署到 VPS 的完整流程记录

Hugo 博客自动部署到 VPS 的完整流程记录

从本地 Hugo 模板搭建,到 GitHub Actions 自动发布,再到 VPS、Nginx、HTTPS 配置的一次完整实践总结。

这篇笔记完整记录了我把一个本地 Hugo 博客部署到 VPS 的全过程,包括:

  • 本地 Hugo 模板初始化与基础配置
  • GitHub 仓库与 Actions 自动部署
  • VPS 上的目录规划与 Nginx 配置
  • 域名解析与 HTTPS 证书申请
  • 常见问题排查
  • 这个过程中经常使用的命令与含义

最终实现的效果是:本地写文章并提交到 GitHub 后,GitHub Actions 自动构建静态站点并发布到 VPS,网页内容自动更新。

一、目标与整体思路

我想实现的目标非常明确:

  1. 在本地维护 Hugo 博客内容
  2. 通过 git push 将更新提交到 GitHub
  3. GitHub Actions 自动构建 Hugo 站点
  4. 将生成后的静态文件自动同步到 VPS
  5. 由 Nginx 直接对外提供网页服务
  6. 配置 HTTPS,让 phyweicheng.comwww.phyweicheng.com 都可以安全访问

整个流程可以概括为:

1
2
3
4
5
6
7
本地修改文章
    -> git commit / git push
    -> GitHub Actions 自动构建 Hugo
    -> 通过 SSH/SCP 上传静态文件到 VPS
    -> VPS 切换 current 软链接到最新版本
    -> Nginx 读取 current 目录
    -> 网站自动更新

二、本地 Hugo 模板部署

1. 准备 Hugo 博客项目

首先需要在本地准备一个 Hugo 项目。我这里使用的是 Hugo Theme Stack 模板,并保留了标准的 Hugo 目录结构,例如:

1
2
3
4
5
6
config/
content/
layouts/
assets/
themes/
.github/

其中最关键的是:

  • content/:文章内容
  • config/:站点配置
  • .github/workflows/:GitHub Actions 自动部署配置

2. 设置生产站点地址

在 Hugo 中,生产站点的域名需要通过 baseurl 指定。我在配置文件中设置为:

1
baseurl = "https://www.phyweicheng.com/"

对应文件:

这个值非常重要,因为 Hugo 在构建时会根据它生成页面中的链接、资源路径和 canonical URL。

3. 保持 public/ 不提交到仓库

Hugo 构建后的静态文件默认输出到 public/。这部分不需要提交到 GitHub,而是交给 GitHub Actions 自动构建。

因此 .gitignore 中应当忽略:

1
public

这样可以避免把编译产物混入源码仓库。

三、将博客与 GitHub 仓库连接

1. 初始化 Git 仓库并提交

如果项目还没有连接 GitHub,基本流程如下:

1
2
3
4
5
6
git init
git add .
git commit -m "init hugo blog"
git branch -M main
git remote add origin <your-github-repo-url>
git push -u origin main

完成后,本地博客项目就与 GitHub 仓库建立了连接。

2. 为什么要用 GitHub Actions

这里没有选择“在 VPS 上直接 git pull 并构建 Hugo”,而是选择让 GitHub Actions 来完成构建,原因是:

  • VPS 不需要安装 Hugo
  • VPS 不需要保留完整源码仓库
  • 构建过程统一由 GitHub 执行,结果更稳定
  • VPS 只保存最终静态文件,更适合静态站点部署

四、配置 GitHub Actions 自动部署

1. 自动部署工作流的目标

自动部署的核心思路是:

  1. main 分支收到新的提交
  2. GitHub Actions 自动运行 Hugo 构建
  3. 将构建好的静态文件打包
  4. 通过 SSH 上传到 VPS
  5. 在 VPS 上解压并切换到最新版本

2. 工作流文件位置

自动部署工作流文件放在:

这个工作流主要做了下面几件事:

  • checkout 当前仓库
  • 安装 Go
  • 安装 Hugo Extended
  • 执行 hugo --gc --minify
  • public/ 打成压缩包
  • 使用 appleboy/scp-action 上传到 VPS
  • 使用 appleboy/ssh-action 在 VPS 上切换部署版本

3. 为什么要用 releasescurrent

在 VPS 上,我采用了这样的目录结构:

1
2
3
/var/www/phyweicheng-blog/
    releases/
    current -> /var/www/phyweicheng-blog/releases/<commit-sha>

每次发布都会:

  1. 生成一个新的版本目录
  2. 把静态文件解压进去
  3. current 软链接切换到新版本

这种方案的好处是:

  • 发布过程清晰
  • 方便回滚
  • Nginx 永远读取固定路径 current

五、配置 GitHub Actions 所需 Secrets

在 GitHub 仓库中进入:

1
Settings -> Secrets and variables -> Actions

然后新增或确认以下 Repository secrets

1. VPS_IP

VPS 的公网 IP 地址。

2. VPS_USER

GitHub Actions 登录 VPS 时使用的 Linux 用户名。

如果直接使用 root 登录,可以写:

1
root

3. VPS_PORT

SSH 登录端口。如果服务器 SSH 不是默认的 22,这里需要填实际端口,例如:

1
6626

4. VPS_DEPLOY_PATH

站点部署目录,例如:

1
/var/www/phyweicheng-blog

5. SSH_PRIVATE_KEY

GitHub Actions 用于连接 VPS 的私钥内容。通常需要把本地或服务器生成的一对 SSH 密钥中的私钥完整粘贴到这里。

六、VPS 上的目录准备

1. 创建部署目录

如果使用 root 部署,可以直接执行:

1
2
sudo mkdir -p /var/www/phyweicheng-blog/releases
sudo chown -R root:root /var/www/phyweicheng-blog

如果使用普通用户,例如 deploy,则应改为:

1
sudo chown -R deploy:deploy /var/www/phyweicheng-blog

2. 检查 SSH 连通性

在正式自动部署前,最好先确认 GitHub 与 VPS 的 SSH 连通已经打通。

如果 GitHub Actions 能顺利执行远程命令,说明:

  • VPS_IP 正确
  • VPS_USER 正确
  • SSH_PRIVATE_KEY 正确
  • VPS_PORT 正确

七、配置 Nginx 站点

1. 站点配置文件

我的 VPS 没有使用 sites-available / sites-enabled 结构,因此采用了 conf.d 的方式。

配置文件路径:

1
/etc/nginx/conf.d/phyweicheng-blog.conf

2. 初始 HTTP 配置

在申请证书之前,先配置 HTTP 站点:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
server {
    listen 80;
    server_name phyweicheng.com www.phyweicheng.com;

    root /var/www/phyweicheng-blog/current;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

保存后执行:

1
2
sudo nginx -t
sudo systemctl reload nginx

只要域名已经解析到 VPS,这时就应该能通过 HTTP 访问网页。

八、域名解析配置

为了让两个域名都能访问,需要在域名服务商后台配置 DNS 解析。

推荐至少配置两条 A 记录:

1
2
A   @      -> VPS公网IP
A   www    -> VPS公网IP

含义如下:

  • @ 表示裸域 phyweicheng.com
  • www 表示 www.phyweicheng.com

如果只配置了 www,那么通常只有 www.phyweicheng.com 可以访问,而 phyweicheng.com 无法访问。

九、申请 HTTPS 证书

1. 安装 Certbot

我的系统是 Debian,因此安装方式如下:

1
2
sudo apt update
sudo apt install certbot python3-certbot-nginx -y

2. 申请两个域名的证书

执行:

1
sudo certbot --nginx -d phyweicheng.com -d www.phyweicheng.com

这个命令会完成以下工作:

  • 申请 Let’s Encrypt 证书
  • 自动修改 Nginx 配置
  • phyweicheng.comwww.phyweicheng.com 绑定证书
  • 可选开启 HTTP 到 HTTPS 的自动跳转

3. 证书申请后检查

申请完成后,可以执行:

1
2
3
4
sudo nginx -t
sudo systemctl reload nginx
sudo ls -la /etc/letsencrypt/live/
sudo ls -la /etc/letsencrypt/live/phyweicheng.com/

如果能看到:

  • fullchain.pem
  • privkey.pem

说明证书已经生成成功。

十、HTTPS 访问失败时的一个真实问题

在本次部署过程中,曾经出现过一个很典型的问题:

  • 证书申请成功
  • Nginx 配置正常
  • 但浏览器访问 https://www.phyweicheng.com 仍然失败

最后排查发现,并不是证书有问题,而是:

1
443 端口被 xray 占用了

通过以下命令可以检查是谁占用了 443:

1
sudo ss -tlnp | grep 443

如果看到是 xray 而不是 nginx,就说明 HTTPS 请求没有交给 Nginx 处理。

解决方法是:

  1. 修改 xray 配置,将其监听端口从 443 改到其他端口,例如 8443
  2. 在客户端同步修改 xray 端口
  3. 放行新的端口,例如 8443
  4. 重新启动 xray 和 nginx

相关命令例如:

1
2
3
4
sudo systemctl restart xray
sudo systemctl restart nginx
sudo ufw allow 8443/tcp
sudo ufw reload

十一、自动续期是否会生效

只要使用的是 Debian 上标准安装的 certbot,通常会自动配置续期机制。

可以通过以下命令检查:

1
2
3
systemctl list-timers | grep certbot
sudo systemctl status certbot.timer
sudo certbot renew --dry-run

它们的作用分别是:

  • 查看是否存在 Certbot 定时任务
  • 查看续期定时器是否已启用
  • 模拟一次证书续期,确认自动续期链路没问题

十二、首次上线后的完整发布流程

当以上步骤全部完成后,后续每次更新博客的流程就非常简单:

1. 在本地修改文章或页面

例如新建文章、修改配置、调整样式等。

2. 本地测试 Hugo

1
hugo server

或者正式构建检查:

1
hugo --gc --minify

3. 提交到 GitHub

1
2
3
git add .
git commit -m "update blog post"
git push origin main

4. GitHub Actions 自动执行部署

推送成功后,GitHub 会自动:

  • 构建 Hugo 站点
  • 上传 public/ 产物到 VPS
  • 切换 current 到最新版本

5. Nginx 自动提供新内容

因为 Nginx 的 root 始终指向:

1
/var/www/phyweicheng-blog/current

所以网站内容会自动更新,不需要登录 VPS 手动 git pull 或手动复制文件。

十三、常见问题总结

1. GitHub Actions checkout 失败

可能原因:

  • 主题目录存在残留 submodule 信息
  • 仓库里有 gitlink,但没有 .gitmodules

解决思路:

  • 不依赖错误的 submodule checkout
  • 改用 Hugo module 的方式引入主题

2. phyweicheng.com 能否访问取决于 DNS

如果只有 www.phyweicheng.com 能访问,通常是因为只解析了 www,没有解析裸域 @

3. https:// 无法访问但 http:// 正常

重点检查:

  • Nginx 是否监听 443
  • 443 是否被其他程序占用
  • 防火墙是否放行 443
  • 云厂商安全组是否放行 443

4. xray 改端口后仍然无法连接

重点检查:

  • xray 是否真的监听了新端口
  • ufw 和云安全组是否放行新端口
  • 客户端端口是否同步修改

十四、这个过程中常用的命令汇总

下面把整个部署过程中最常用的命令整理一下。

1. Git 相关

1
git add .

将当前修改加入暂存区。

1
git commit -m "message"

生成一次本地提交记录。

1
git push origin main

将本地提交推送到 GitHub 的 main 分支,并触发自动部署。

2. Hugo 相关

1
hugo server

在本地启动开发服务器,方便预览博客效果。

1
hugo --gc --minify

执行正式构建:

  • --gc:清理无用缓存和资源
  • --minify:压缩输出内容

3. Nginx 相关

1
sudo nginx -t

检查 Nginx 配置语法是否正确。

1
sudo systemctl reload nginx

平滑重载 Nginx 配置,不中断服务。

1
sudo systemctl restart nginx

重启 Nginx 服务,适合在重大配置变化后使用。

4. Certbot 相关

1
sudo certbot --nginx -d phyweicheng.com -d www.phyweicheng.com

为两个域名申请证书,并自动写入 Nginx HTTPS 配置。

1
sudo certbot renew --dry-run

模拟续期,验证自动续期逻辑是否正常。

5. 端口与服务排查

1
sudo ss -tlnp | grep 443

检查谁正在监听 443 端口。

1
sudo systemctl status nginx

查看 Nginx 当前状态。

1
sudo systemctl status xray

查看 xray 当前状态。

1
sudo journalctl -u xray -n 50 --no-pager

查看 xray 最近的日志,便于定位启动失败和配置错误。

6. 防火墙相关

1
sudo ufw status

查看当前防火墙规则。

1
2
3
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 8443/tcp

分别放行 HTTP、HTTPS 和 xray 的自定义端口。

1
sudo ufw reload

重新加载防火墙规则。

7. 证书文件检查

1
2
sudo ls -la /etc/letsencrypt/live/
sudo ls -la /etc/letsencrypt/live/phyweicheng.com/

确认证书目录和文件是否存在。

十五、总结

这次部署的关键点并不只是“把网页放上去”,而是把整套链路打通:

  • 本地负责内容维护
  • GitHub 负责自动构建与自动发布
  • VPS 负责稳定托管静态文件
  • Nginx 负责网站访问
  • Certbot 负责 HTTPS

当这套流程跑通以后,博客维护就会轻松很多。以后更新文章时,只需要:

1
2
3
git add .
git commit -m "update note"
git push origin main

剩下的构建、上传、切换版本、网页更新,都会自动完成。

如果以后继续扩展,还可以在这套基础上增加:

  • 评论系统
  • 访问统计
  • 备份与回滚脚本
  • 多环境部署
  • 自动化健康检查

但对于一个 Hugo 静态博客来说,这样的部署方案已经足够稳定、清晰,也非常适合长期维护。