Hermes Agent 自动备份教程:Rclone + Cloudflare R2 + Cron 每日异地备份

Hermes Agent 的配置、会话、记忆、技能和凭据大多放在 ~/.hermes。VPS 一旦被封、误删或磁盘损坏,重新安装程序不难,真正麻烦的是把这套长期积累的状态找回来。

下面这套方案用 Rclone 把 ~/.hermes 打包、加密并上传到 Cloudflare R2,再交给 Linux 系统的 Cron 每天执行。它不依赖 Hermes 自己的定时任务:即使 Hermes Agent 临时故障,系统 Cron 仍能继续运行备份。

先说两个修正:不要把加密密码直接写进脚本,也不要用 pkill -f "hermes start" 粗暴结束进程。前者会泄露密码,后者可能杀掉错误进程,而且原方案没有可靠地把 Hermes 重启。

一、准备 Cloudflare R2

进入 Cloudflare 控制台,打开 R2 对象存储,完成下面三件事:

  1. 记录 Cloudflare Account ID。
  2. 创建一个私有存储桶,例如 hermes-backup-bucket
  3. 在“管理 R2 API 令牌”中创建令牌,授予目标存储桶的对象读取、写入和删除权限。

保存好生成的 Access Key ID 和 Secret Access Key。Secret 只会完整显示一次,不要发到聊天窗口,也不要写入公开仓库。

二、安装并配置 Rclone

官方安装脚本适合 Debian、Ubuntu 等常见 Linux 发行版:

curl -fsSL https://rclone.org/install.sh | sudo bash
rclone version

如果不想执行网络脚本,也可以通过系统包管理器安装,但仓库里的版本可能偏旧。

启动交互式配置:

rclone config

按下面的值填写。Rclone 不同版本的菜单编号可能变化,所以应看选项名称,不要死记数字。

  1. 选择 n 新建 remote,名称填写 cf_r2
  2. 存储类型选择 s3
  3. Provider 选择 Cloudflare
  4. 填写 Access Key ID 和 Secret Access Key。
  5. Endpoint 填写 https://<ACCOUNT_ID>.r2.cloudflarestorage.com,把占位符换成真实 Account ID。
  6. Region 保持 auto 或留空;ACL 保持 private
  7. 保存配置并输入 q 退出。

测试连接:

rclone lsd cf_r2:
rclone lsf cf_r2:hermes-backup-bucket

第一条命令能列出存储桶,第二条没有报错,就说明认证和桶权限正常。

三、单独保存加密密码

创建只允许当前用户读取的密码文件。下面的示例字符串必须替换,建议使用密码管理器生成至少 24 位随机密码。

mkdir -p "$HOME/.config/hermes-backup"
umask 077
printf '%s\n' '请替换为你自己的长随机密码' \
  > "$HOME/.config/hermes-backup/passphrase"
chmod 600 "$HOME/.config/hermes-backup/passphrase"

这个密码文件不能放进 ~/.hermes,否则恢复时会陷入“没有密码就打不开包含密码的备份”这个死循环。请把密码再保存一份到本地密码管理器或离线介质。

四、创建安全版备份脚本

脚本建议放在当前用户目录,而不是直接写进 /opt。这样不需要让普通备份任务持有 root 权限。

mkdir -p "$HOME/bin"
nano "$HOME/bin/hermes-r2-backup.sh"

粘贴以下完整脚本:

#!/usr/bin/env bash
set -Eeuo pipefail
umask 077

PATH="$HOME/.local/bin:/usr/local/bin:/usr/bin:/bin"
REMOTE_NAME="cf_r2"
BUCKET_PATH="hermes-backup-bucket/hermes-agent"
RETENTION_DAYS=30
HERMES_DIR="${HERMES_HOME:-$HOME/.hermes}"
PASSWORD_FILE="$HOME/.config/hermes-backup/passphrase"
LOCK_FILE="${TMPDIR:-/tmp}/hermes-r2-backup.lock"

command -v rclone >/dev/null || { echo "rclone 未安装"; exit 1; }
command -v openssl >/dev/null || { echo "openssl 未安装"; exit 1; }
[[ -d "$HERMES_DIR" ]] || { echo "目录不存在: $HERMES_DIR"; exit 1; }
[[ -s "$PASSWORD_FILE" ]] || { echo "密码文件不存在: $PASSWORD_FILE"; exit 1; }

exec 9>"$LOCK_FILE"
flock -n 9 || { echo "已有备份任务正在运行,本次退出"; exit 0; }

DATE_TAG="$(date -u +%Y%m%d_%H%M%S)"
TMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/hermes-backup.XXXXXX")"
BACKUP_FILE="$TMP_DIR/hermes_${DATE_TAG}.tar.gz.enc"
REMOTE_DIR="${REMOTE_NAME}:${BUCKET_PATH}"
GATEWAY_WAS_ACTIVE=0

cleanup() {
  rc=$?
  rm -rf -- "$TMP_DIR"
  if [[ "$GATEWAY_WAS_ACTIVE" -eq 1 ]]; then
    hermes gateway start >/dev/null 2>&1 || true
  fi
  exit "$rc"
}
trap cleanup EXIT INT TERM

# 官方 gateway 服务运行时先正常停止,避免复制写入中的 SQLite 数据库。
if command -v systemctl >/dev/null \
  && systemctl --user is-active --quiet hermes-gateway.service; then
  GATEWAY_WAS_ACTIVE=1
  hermes gateway stop
fi

# -C 很重要:归档内保存的是 .hermes/...,恢复时不会多出 home/user 目录。
tar -C "$(dirname "$HERMES_DIR")" -czf - "$(basename "$HERMES_DIR")" \
  | openssl enc -aes-256-cbc -salt -pbkdf2 -iter 200000 \
      -pass "file:$PASSWORD_FILE" -out "$BACKUP_FILE"

# copyto 明确指定远端文件名,避免路径歧义。
rclone copyto "$BACKUP_FILE" "$REMOTE_DIR/$(basename "$BACKUP_FILE")" \
  --s3-no-check-bucket

# 回读远端对象;失败时脚本返回非零,不会把“上传失败”当成成功。
rclone lsf "$REMOTE_DIR" --files-only | grep -Fx "$(basename "$BACKUP_FILE")" >/dev/null

# 删除超过保留期的旧文件,并清理空目录。
rclone delete "$REMOTE_DIR" --min-age "${RETENTION_DAYS}d"
rclone rmdirs "$REMOTE_DIR" --leave-root

printf 'Backup %s finished: %s\n' "$DATE_TAG" "$(basename "$BACKUP_FILE")"

保存后赋予执行权限:

chmod 700 "$HOME/bin/hermes-r2-backup.sh"

先手动执行一次:

"$HOME/bin/hermes-r2-backup.sh"
rclone lsl cf_r2:hermes-backup-bucket/hermes-agent

看到以 hermes_年月日_时间.tar.gz.enc 命名的对象,才算完成第一次验证。

五、设置每日 Cron

编辑当前 Hermes 用户的 crontab,不要用 root 的 crontab,否则 $HOME、Rclone 配置和 Hermes 目录都会指向错误用户。

crontab -e

每天凌晨 2 点运行:

0 2 * * * "$HOME/bin/hermes-r2-backup.sh" >> "$HOME/.hermes/hermes_backup.log" 2>&1

每 12 小时运行一次:

0 */12 * * * "$HOME/bin/hermes-r2-backup.sh" >> "$HOME/.hermes/hermes_backup.log" 2>&1

检查任务和日志:

crontab -l
tail -n 100 "$HOME/.hermes/hermes_backup.log"

这里用的是 Linux 系统 Cron,不是 hermes cron。备份属于灾难恢复基础设施,应该尽量独立于被备份的应用。

六、新 VPS 上完整恢复

新机器先安装 Hermes Agent、Rclone 和 OpenSSL,再重新配置同名的 cf_r2 remote。不要直接运行 hermes setup 覆盖旧配置。

curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash
curl -fsSL https://rclone.org/install.sh | sudo bash
rclone config

创建密码文件,并填入备份时使用的同一个密码:

mkdir -p "$HOME/.config/hermes-backup"
umask 077
printf '%s\n' '你的原加密密码' \
  > "$HOME/.config/hermes-backup/passphrase"
chmod 600 "$HOME/.config/hermes-backup/passphrase"

自动找出时间戳最新的备份并下载:

REMOTE_DIR="cf_r2:hermes-backup-bucket/hermes-agent"
LATEST="$(rclone lsf "$REMOTE_DIR" --files-only | sort | tail -n 1)"
[[ -n "$LATEST" ]] || { echo "没有找到备份"; exit 1; }
rclone copyto "$REMOTE_DIR/$LATEST" "$HOME/$LATEST"

解密到临时目录并检查结构:

RESTORE_DIR="$(mktemp -d)"
openssl enc -d -aes-256-cbc -pbkdf2 -iter 200000 \
  -pass "file:$HOME/.config/hermes-backup/passphrase" \
  -in "$HOME/$LATEST" -out "$RESTORE_DIR/hermes.tar.gz"

tar -xzf "$RESTORE_DIR/hermes.tar.gz" -C "$RESTORE_DIR"
test -f "$RESTORE_DIR/.hermes/config.yaml"
ls -la "$RESTORE_DIR/.hermes"

确认目录无误后再替换。先保留当前目录,出错时还能回滚:

hermes gateway stop || true
[[ -d "$HOME/.hermes" ]] \
  && mv "$HOME/.hermes" "$HOME/.hermes.before-restore-$(date +%s)"
mv "$RESTORE_DIR/.hermes" "$HOME/.hermes"
chmod -R go-rwx "$HOME/.hermes"

hermes doctor
hermes gateway start
hermes gateway status

恢复后检查模型凭据、Telegram/Discord 等频道、Cron 任务和技能是否正常。系统 crontab 与 Rclone 配置不在 ~/.hermes 中,所以新机上仍需重新配置。

七、上线前必须做的四个测试

  1. 上传测试:手动运行脚本并在 R2 中确认对象大小不是 0。
  2. 解密测试:下载一份备份,在临时目录解密并确认能看到 .hermes/config.yaml
  3. Cron 测试:临时把计划改为几分钟后执行,确认非交互环境下也能找到 rclone 和 Hermes。
  4. 恢复演练:至少在另一台测试机完整恢复一次。没有做过恢复演练的备份,只能算“可能有用”。

R2 端还可以设置生命周期规则作为第二道保留策略。无论是否启用云端自动清理,都建议每月下载一份到本地硬盘。R2 解决的是 VPS 单点故障,本地副本则防止账号、令牌或云存储本身出现问题。

参考资料

zh_CNChinese