Hermes Agent 的配置、会话、记忆、技能和凭据大多放在 ~/.hermes。VPS 一旦被封、误删或磁盘损坏,重新安装程序不难,真正麻烦的是把这套长期积累的状态找回来。
下面这套方案用 Rclone 把 ~/.hermes 打包、加密并上传到 Cloudflare R2,再交给 Linux 系统的 Cron 每天执行。它不依赖 Hermes 自己的定时任务:即使 Hermes Agent 临时故障,系统 Cron 仍能继续运行备份。
先说两个修正:不要把加密密码直接写进脚本,也不要用 pkill -f "hermes start" 粗暴结束进程。前者会泄露密码,后者可能杀掉错误进程,而且原方案没有可靠地把 Hermes 重启。
一、准备 Cloudflare R2
进入 Cloudflare 控制台,打开 R2 对象存储,完成下面三件事:
- 记录 Cloudflare Account ID。
- 创建一个私有存储桶,例如
hermes-backup-bucket。 - 在“管理 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 不同版本的菜单编号可能变化,所以应看选项名称,不要死记数字。
- 选择
n新建 remote,名称填写cf_r2。 - 存储类型选择
s3。 - Provider 选择
Cloudflare。 - 填写 Access Key ID 和 Secret Access Key。
- Endpoint 填写
https://<ACCOUNT_ID>.r2.cloudflarestorage.com,把占位符换成真实 Account ID。 - Region 保持
auto或留空;ACL 保持private。 - 保存配置并输入
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 中,所以新机上仍需重新配置。
七、上线前必须做的四个测试
- 上传测试:手动运行脚本并在 R2 中确认对象大小不是 0。
- 解密测试:下载一份备份,在临时目录解密并确认能看到
.hermes/config.yaml。 - Cron 测试:临时把计划改为几分钟后执行,确认非交互环境下也能找到 rclone 和 Hermes。
- 恢复演练:至少在另一台测试机完整恢复一次。没有做过恢复演练的备份,只能算“可能有用”。
R2 端还可以设置生命周期规则作为第二道保留策略。无论是否启用云端自动清理,都建议每月下载一份到本地硬盘。R2 解决的是 VPS 单点故障,本地副本则防止账号、令牌或云存储本身出现问题。











