mcpField
H
haos-cloud-backup

haos-cloud-backup

github.com/caanio云与 DevOps036 次安装1 天前更新
代码仓库

关于 haos-cloud-backup

此技能介绍如何用 rclone 将 Home Assistant OS 备份传到云存储,涵盖上传限速、按日期保留备份及验证流程。

主要功能

  1. 01配置 rclone 备份任务
  2. 02按带宽限制上传速度
  3. 03按日期实施 GFS 保留
  4. 04用 dry_run 测试删除规则
  5. 05验证云端副本与计划

适用场景

  1. 01将 Home Assistant 日常备份移出 SD 卡
  2. 02限制备份占用家庭上行带宽
  3. 03保留指定的每日、每周和每月备份
  4. 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 的 REST info 端点会以未屏蔽的形式返回 password 类型的选项(haos-addon-deploy §4),而字段名称不一定表明它是机密信息。
  • 执行完此代码块后,在通过 ha apps info --raw-json(确认 "dry_run": false,且作业完整)和日志中的 scheduled jobs: 列表进行验证期间,请保留更改前的 opts.json 作为回滚副本。两项检查都通过后,再运行 rm -f opts.json payload.json。

评论

支持 Markdown