目录

使用 GitHub Actions + rsync/SSH 实现 Hugo 自动部署

由于本博客之前还是手动部署,忍受一段时间后实在受不了了,还是开启了 CI/CD 日子~

目标

实现:

本地写文章 → git push 到 GitHub → GitHub Actions 自动构建 Hugo → rsync 部署到服务器

推荐结构:

GitHub 仓库:保存 Hugo 源码
GitHub Actions:负责构建 public/
deploy 用户:负责 SSH 登录和部署
www / www-data 用户:负责运行网站服务,例如 Nginx

一、服务器准备

以 Debian / Ubuntu 为例,本文默认你已经安装了 nginx

1. 安装依赖

sudo apt update
sudo apt install -y rsync

Hugo 可以在 GitHub Actions 里构建,所以服务器不需要安装 Hugo。


二、创建 deploy 用户

不要使用 root 部署,也不建议让 www 用户登录 SSH。

sudo useradd -m -s /bin/bash deploy

如果已经有 deploy 用户,可以检查:

getent passwd deploy

最后一列应该类似:

/bin/bash

如果是:

/usr/sbin/nologin

可以改成:

sudo usermod -s /bin/bash deploy

三、网站目录权限配置

假设网站目录是:

/var/www/blog

创建目录:

sudo mkdir -p /var/www/blog

如果 Nginx 使用 www 用户:

sudo usermod -aG www deploy
sudo chown -R www:www /var/www/blog
sudo chmod -R 775 /var/www/blog
sudo find /var/www/blog -type d -exec chmod g+s {} \;
deploy 用户:可以写入 /var/www/blog
www 用户:可以读取网站文件

四、配置 SSH 密钥登录

1. 本地生成部署密钥

在本地电脑执行:

ssh-keygen -t ed25519 -C "github-actions-hugo" -f github-actions-hugo

会生成:

github-actions-hugo
github-actions-hugo.pub

其中:

github-actions-hugo      私钥,放到 GitHub Secrets
github-actions-hugo.pub  公钥,放到服务器 deploy 用户

2. 把公钥注册到 deploy 用户

在服务器上执行:

sudo mkdir -p /home/deploy/.ssh
sudo cat github-actions-hugo.pub >> /home/deploy/.ssh/authorized_keys

github-actions-hugo.pub 的内容粘进去。

然后修复权限:

sudo chown -R deploy:deploy /home/deploy/.ssh
sudo chmod 700 /home/deploy/.ssh
sudo chmod 600 /home/deploy/.ssh/authorized_keys

3. 本地测试 SSH

ssh -i ./github-actions-hugo deploy@你的服务器IP

如果可以登录,说明密钥配置成功。

如果报错类似:

Permission denied
未注册用户密钥

通常是:

公钥没有放到 /home/deploy/.ssh/authorized_keys
authorized_keys 权限不对
.ssh 目录权限不对
文件归属不是 deploy:deploy
登录用户写错

五、配置 GitHub Secrets

进入 GitHub 仓库:

Settings → Secrets and variables → Actions → New repository secret

添加:

SERVER_HOST=你的服务器IP或域名
SERVER_USER=deploy
SERVER_SSH_KEY=私钥内容
SERVER_PATH=/var/www/blog

六、添加 GitHub Actions 配置

在 Hugo 仓库中新建文件:

.github/workflows/deploy.yml

内容如下:

name: Deploy Hugo Blog

on:
  push:
    branches:
      - main

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          submodules: recursive

      - name: Setup Hugo
        uses: peaceiris/actions-hugo@v3
        with:
          hugo-version: latest
          extended: true

      - name: Build
        run: hugo --minify --cleanDestinationDir

      - name: Setup SSH
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.SERVER_SSH_KEY }}" > ~/.ssh/deploy_key
          chmod 600 ~/.ssh/deploy_key
          ssh-keyscan -H "${{ secrets.SERVER_HOST }}" >> ~/.ssh/known_hosts

      - name: Test SSH connection
        run: |
          ssh -o ConnectTimeout=20 -i ~/.ssh/deploy_key \
            ${{ secrets.SERVER_USER }}@${{ secrets.SERVER_HOST }} \
            "echo connected"

      - name: Deploy with rsync
        run: |
          rsync -rlvz --delete \
            -e "ssh -i ~/.ssh/deploy_key" \
            public/ \
            ${{ secrets.SERVER_USER }}@${{ secrets.SERVER_HOST }}:${{ secrets.SERVER_PATH }}/

ps: 如果是宝塔,记得在最后一栏 rsync -rlvz --delete 内添加排查.user.ini,否则会提示运行失败。

      - name: Deploy with rsync
        run: |
          rsync -rlvz --delete \
            --exclude='.user.ini' \
            .......

如果服务器 SSH 非 22 端口

假设 SSH 端口是 2222,修改 rsync:

rsync -avz --delete \
  -e "ssh -i ~/.ssh/deploy_key -p 2222" \
  public/ \
  ${{ secrets.SERVER_USER }}@${{ secrets.SERVER_HOST }}:${{ secrets.SERVER_PATH }}/


七、可选:限制 deploy 用户只能 rsync 到指定目录

如果不想让 deploy 用户正常登录 shell,可以使用 rrsync 限制它只能访问 /var/www/blog

安装:

sudo apt install -y rrsync

查看路径:

which rrsync

通常是:

/usr/bin/rrsync

编辑 deploy 的 authorized_keys:

sudo nano /home/deploy/.ssh/authorized_keys

把原来的公钥:

ssh-ed25519 AAAA... github-actions-hugo

改成:

command="/usr/bin/rrsync /var/www/blog",no-agent-forwarding,no-port-forwarding,no-pty,no-X11-forwarding ssh-ed25519 AAAA... github-actions-hugo

这样这个密钥只能用于 rsync 部署,不能打开交互式 SSH 终端。


八、发布文章流程

以后只需要:

git add .
git commit -m "new post"
git push origin main

GitHub Actions 会自动:

1. 拉取 Hugo 源码
2. 安装 Hugo
3. 构建 public/
4. 通过 SSH 连接服务器
5. 用 rsync 同步 public/ 到 /var/www/blog
6. 网站自动更新

九、常见错误排查

1. SSH 超时

错误:

ssh: connect to host xxx port 22: Connection timed out
rsync error: unexplained error (code 255)

原因通常是:

服务器防火墙没开放 22
云服务器安全组没开放 22
SSH 端口不是 22
SERVER_HOST 填错
服务器只允许特定 IP 登录

检查服务器防火墙:

sudo ufw status
sudo ufw allow 22/tcp
sudo ufw reload

云服务器还要检查控制台安全组,开放:

TCP 22
来源 0.0.0.0/0

2. 密钥未注册 / Permission denied

原因通常是 deploy 用户没有配置公钥。

修复:

sudo mkdir -p /home/deploy/.ssh
sudo nano /home/deploy/.ssh/authorized_keys
sudo chown -R deploy:deploy /home/deploy/.ssh
sudo chmod 700 /home/deploy/.ssh
sudo chmod 600 /home/deploy/.ssh/authorized_keys

3. deploy 无法写入网站目录

检查权限:

ls -ld /var/www/blog

如果 Nginx 用户是 www-data

sudo usermod -aG www-data deploy
sudo chown -R www-data:www-data /var/www/blog
sudo chmod -R 775 /var/www/blog
sudo find /var/www/blog -type d -exec chmod g+s {} \;

4. Hugo 主题没有被拉下来

如果主题使用 Git submodule,确保 workflow 里有:

with:
  submodules: recursive

5. Hugo 主题需要 extended 版本

很多主题需要 Hugo Extended,确保:

with:
  extended: true