关于 haos-cloud-backup
此技能介绍如何用 rclone 将 Home Assistant OS 备份传到云存储,涵盖上传限速、按日期保留备份及验证流程。
主要功能
- 01配置 rclone 备份任务
- 02按带宽限制上传速度
- 03按日期实施 GFS 保留
- 04用 dry_run 测试删除规则
- 05验证云端副本与计划
适用场景
- 01将 Home Assistant 日常备份移出 SD 卡
- 02限制备份占用家庭上行带宽
- 03保留指定的每日、每周和每月备份
- 04删除文件前检查保留筛选规则
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ caanio/ agent-skills --skill haos-cloud-backup安装后在 Claude Code 中输入/haos-cloud-backup确认已加载。
详细介绍
HAOS 备份 → 云端(rclone、限速、GFS 保留策略)
将 HA 的每日自动备份从唯一的 SD 卡转存到云存储,避免占满家庭上行带宽,并自动清理本地和云端的旧备份。
已于 2026-07-11 在 Raspberry Pi 4(HAOS 18.1、Core 2026.6.4)上实测通过 jcwillox/hassio-rclone-backup v3.4.1 → Google Drive。⚠️ 标记的是实际容易踩到的坑。
ha = 按 haos-addon-deploy §0/§8 配置的 SSH 别名(适用 bash -lc 规则)。
0. 为什么采用这种方案
- ⚠️ 只有 rclone 可以限速。 原生
google_drive备份代理没有带宽选项,sabeechen/hassio-google-drive-backup 已将限速请求关闭为不计划实现(issue #366)。如果上传速度很重要,rclone 是唯一选择:--bwlimit使用 MBytes/s(1.5M= 12 Mbit/s——合理的上限约为实测上行带宽的 30%)。 - rclone 没有内置的祖父-父亲-儿子(GFS)保留策略,但该加载项会将日期嵌入每个备份的文件名
(
Automatic_backup_<ver>_YYYY-MM-DD_HH.MM_<id>.tar,§3),因此可通过日期筛选规则模拟此策略(§4)。 - 将上传安排在 HA 自动备份时间之后(默认约 05:00 → 05:30 上传):避开高峰时段,并且每天只传输一个新文件。
1. 安装加载项
ssh ha 'bash -lc "ha store add https://github.com/jcwillox/hassio-rclone-backup && ha store reload"'
ssh ha 'bash -lc "ha apps install 19a172aa_rclone_backup"'
这是单加载项仓库;安装后的 slug 为 19a172aa_rclone_backup(哈希由仓库 URL 固定)。⚠️ 使用 ha store add,而不是 add-repository——关于容易导致静默错误的子命令,参见 haos-https-tunnel §2。
2. 云端身份验证(rclone.conf)
⚠️ 先有鸡还是先有蛋:加载项启动时会验证每个任务的远程端;如果缺少 rclone.conf 或尚未定义远程端,就会以 FATAL 状态停止。在远程端配置完成前,请保留 jobs: [](否则会出现 state: error——虽然无害,但容易造成困惑)。
配置文件位于 /homeassistant/rclone.conf(与 configuration.yaml 在同一目录;这是加载项 config_path 的默认路径)。有两种配置方式,以及在任务创建后容易踩到的两个坑:
-
Web UI 方式:启动加载项(任务列表为空)→ 打开 Web UI。⚠️ 登录页面只是装饰——GUI 以
--rc-no-auth运行;保留预填的 URL,用户名和密码都留空,然后点击 Login。进入 Configs → 创建远程端。通过 ingress 进行 OAuth 时,本地主机重定向可能失败,因此可采用: -
粘贴令牌方式(更可靠,已在实际环境中使用):在任意带浏览器的机器上运行
rclone authorize "drive"→ 用户完成 Google 授权 → 命令会输出一个令牌 JSON 块。写入配置文件并传到设备:[gdrive] type = drive scope = drive token = {"access_token":"...","refresh_token":"...","expiry":"..."}设置
chmod 600。此后刷新令牌会在设备上自动续期——可以卸载辅助机器上的 rclone。进入容器内部验证,并将容器名称解析一次为$C(在 §4 和 §6 中重复使用);前缀(addon_/app_)由 Supervisor 版本决定,因此需同时匹配两者:C=$(ssh ha "sudo docker ps -a --format '{{.Names}}' \ | grep -E '^(app|addon)_19a172aa_rclone_backup$'") || true [ -n "$C" ] || { echo "no rclone_backup container — add-on installed?" >&2; exit 1; } ssh ha "sudo docker exec $C rclone --config /homeassistant/rclone.conf about gdrive:" -
⚠️ Web UI 只显示 rclone 远程端和实时传输。任务、计划和 bwlimit 属于加载项选项(HA → Settings → Apps → 该加载项 → Configuration 选项卡)——想在 Web UI 中查找这些设置的用户会一无所获。
-
⚠️ 容易被误认为损坏的显示问题,其实并非如此:HA YAML 编辑器可能会将
flags:映射重新显示成 Python repr 字符串 ("{'bwlimit': '1.5M'}")——这只是显示问题;请相信任务日志 (Starting bandwidth limiter at 1.500Mi Byte/s),而不要相信编辑器。同样,日期筛选器中的?字符(*_????-??-01_*)是 rclone 的单字符 glob 通配符,并非乱码——用户常把这两种情况都报告为故障。
当上文的 about gdrive: 检查输出远程端的使用情况而不是错误时,就表示上述任一配置方式已完成;此时才添加任务(§3)。
3. 作业(上传 + 清理)
作业配置在加载项的选项中——§7 中的两种路径都可以。
一些能省下大量调试时间的经验:
- ⚠️
delete作业要将路径放在source中,绝不能放在destination中——加载项会以 FATAL 状态终止,并提示“at least 1 source must be specified”。 flags:是一个映射(下划线会自动转换为短横线)。有顺序要求的--filter规则必须放在extra_flags中——顺序很重要,而include:/exclude:数组无法保证顺序。- 没有
schedule的作业会在加载项启动时运行——适合进行一次性测试,但用于删除作业则很危险。 - 重命名文件才能让基于名称的筛选器生效:HA 原生会将备份存储为
<slug>.tar(8 位十六进制字符,难以辨认)。加载项会在/backup中原地将文件重命名为{name}_{date}_{slug}.tar(日志中会显示renamed/unrenamed N backups;首次运行也会重命名已有备份)——下方及 §4 中的+ Automatic_backup_*筛选器会匹配这些重命名后的名称,本地和远程文件名也会保持一致。这对 HA 没有影响:Supervisor 根据 tar 元数据而非文件名识别备份,因此还原不受影响;末尾的 slug 仍可映射回 HA 备份 ID。 - 先启用顶层
dry_run: true(与jobs:同级),进行一次不花成本的演练。 推荐流程:在第一个计划运行日内保持启用,查看Skipped ... as --dry-run is set日志行,以确认每个作业选中的文件都完全正确;然后设置dry_run: false(通过 §7 中的 SSH)并重启。⚠️ 启用期间,不会上传或删除任何内容,但每个作业都会记录“finished”,乍看日志也一切正常——很容易忘记关闭,一拖就是好几周。
jobs:
- name: upload to cloud
schedule: "30 5 * * *"
command: copy # copy, not sync — never mirror deletions upward
sources: [/backup]
destination: "gdrive:HA-Backups"
flags: { bwlimit: 1.5M }
- name: prune local autos
schedule: "0 7 * * *"
command: delete
source: /backup
flags: { min-age: 7d } # SD card keeps ~7 dailies
extra_flags: ["--filter", "+ Automatic_backup_*", "--filter", "- *"]
- name: prune local others # named/milestone + addon-update backups
schedule: "5 7 * * *"
command: delete
source: /backup
flags: { min-age: 90d }
4. 远程端的 GFS 精简(以日期为基准)
每周保留日期 = 每月 01/08/15/22 日;每月保留日期 = 每月 01 日。不是“每周一”——以日历日期为基准,保留密度相同。筛选规则按先匹配先处理:先保护基准日期,再匹配每日模式,最后删除其余内容。具名备份永远不会匹配该模式,因此会一直保留。
- name: thin to weekly (7d-28d)
schedule: "10 7 * * *"
command: delete
source: "gdrive:HA-Backups"
flags: { min-age: 7d, max-age: 28d }
extra_flags: ["--filter", "- *_????-??-01_*", "--filter", "- *_????-??-08_*",
"--filter", "- *_????-??-15_*", "--filter", "- *_????-??-22_*",
"--filter", "+ Automatic_backup_*", "--filter", "- *"]
- name: thin to monthly (28d-180d)
schedule: "15 7 * * *"
command: delete
source: "gdrive:HA-Backups"
flags: { min-age: 28d, max-age: 180d }
extra_flags: ["--filter", "- *_????-??-01_*",
"--filter", "+ Automatic_backup_*", "--filter", "- *"]
- name: purge (>180d)
schedule: "20 7 * * *"
command: delete
source: "gdrive:HA-Backups"
flags: { min-age: 180d }
extra_flags: ["--filter", "+ Automatic_backup_*", "--filter", "- *"]
信任任何删除作业之前,都要在容器内使用 --dry-run 手动运行其原样命令(sudo docker exec $C rclone …,$C 来自 §2)——并传入与 §2 相同的 --config——然后确认它会删除的每个文件都是该作业预期删除的文件(在其时间范围内、不在该作业保护的任何基准日期上;如果该作业按此筛选,则应为 Automatic_backup_*);任何其他文件都说明筛选器有误。Google Drive 默认会将删除的文件移至垃圾箱(额外提供 30 天的安全保障);只有在配额紧张时才添加 drive-use-trash: false。
5. 清理现有积压备份
如果 /backup 中已有保留策略启用前的积压备份:
- ⚠️ 通过 Supervisor 删除,而不是使用
rm:ha backups remove <slug>——直接使用rm会导致 Supervisor 的备份列表过时。 - ⚠️ Supervisor 列表显示 UTC 日期;文件名显示本地时间——本地时间 05:00 的备份会在列表中显示为前一天。请根据完整 ISO 时间戳(或 slug)匹配,绝不要仅按日期字符串匹配。
- 操作顺序:先确认云端副本存在,再删除本地副本。首次测试上传时,使用
--bwlimit手动运行复制,并将远程文件(rclone ls)的字节大小与源文件进行比较。
6. 端到端验证
ssh ha 'bash -lc "ha apps logs 19a172aa_rclone_backup"' # want: "scheduled jobs:" listing all
ssh ha "sudo docker exec $C rclone --config /homeassistant/rclone.conf ls gdrive:HA-Backups" # $C from §2
scheduled jobs: 列表应包含 §3 和 §4 中的全部六个作业。启用 dry_run: true 时,ls 不会显示任何由作业上传的内容;切换设置(§7)后请重新运行。
一个 611 MB 的备份在 bwlimit 1.5M 时需要约 7 分钟——如果完成得快得多,说明限速未生效(检查作业日志中执行的命令是否带上了该标志)。
7. 从命令行更改选项(例如关闭 dry_run)
对于通过 UI 操作的人来说,常规做法是:Configuration 选项卡 → ⋮ → Edit in YAML → Save → 重启。 本节介绍通过 SSH 进行的无界面/Agent 更改。
⚠️ ha apps 没有 options 子命令——请通过 Supervisor API 设置选项。始终发送完整的选项对象(获取 → 修改 → POST),切勿只发送部分对象:
ssh ha 'bash -lc "ha apps info 19a172aa_rclone_backup --raw-json"' > opts.json
python3 - <<'EOF'
import json
d = json.load(open('opts.json'))['data']['options']
d['dry_run'] = False
json.dump({'options': d}, open('payload.json', 'w'), ensure_ascii=False)
EOF
chmod 600 opts.json payload.json # may hold password-typed options in plaintext — see below
ssh ha 'bash -lc "curl -sS -X POST -H \"Authorization: Bearer \$SUPERVISOR_TOKEN\" \
-H \"Content-Type: application/json\" -d @- \
http://supervisor/addons/19a172aa_rclone_backup/options"' < payload.json
ssh ha 'bash -lc "ha apps restart 19a172aa_rclone_backup"'
- ⚠️ 请将其转义为
\$SUPERVISOR_TOKEN。 远程父 shell 不是登录 shell:未转义的$SUPERVISOR_TOKEN会在其中展开为空值,API 会返回具有误导性的401: Unauthorized(该变量必须在会加载环境变量的bash -lc内展开)。 - 每次更改时都会运行
chmod 600,因为 Supervisor 的 RESTinfo端点会以未屏蔽的形式返回password类型的选项(haos-addon-deploy§4),而字段名称不一定表明它是机密信息。 - 执行完此代码块后,在通过
ha apps info --raw-json(确认"dry_run": false,且作业完整)和日志中的scheduled jobs:列表进行验证期间,请保留更改前的opts.json作为回滚副本。两项检查都通过后,再运行rm -f opts.json payload.json。