将备份作为持续运营的项目,而非一次性任务。基于 restic 的策略封装。
推荐在所有地方使用策略备份:开发环境、个人电脑、生产服务器。
- v0.5.0+: daemon 模式,支持项目级调度配置
- v0.4.0: TypeScript 版本,使用 systemd timer,凭证直接写在 YAML
- v0.3.x: TypeScript 版本,使用 crontab,支持 .env
- v0.2.x: Bash 版本
- 意外修改或删除时,从备份提取
- 硬盘损毁、机房失联时,从备份重建
- 需要追溯历史状态时,查阅备份
可重建的内容不备份:docker 镜像、node_modules、缓存、构建产物等。判断标准不是"是否有价值",而是"能否从其他来源恢复"。
备份保护所有项目,其重要性是所有项目的总和。应作为最重要的项目持续运营。
备份频率应匹配数据变化频率。高频变化时段多备份,低频时段少备份。
未经验证的备份等于没有备份。定期验证备份可恢复,失败时必须有告警。
定期清理旧快照,防止存储膨胀。
同步工具(如 Syncthing)会将误操作实时传播到所有节点。真正的备份必须具备版本历史和回滚能力。
假设本机被入侵,攻击者不应能通过本机凭证定位或删除备份数据。
v0.5.0 架构升级:
- 使用 daemon 模式替代 systemd timer
- 支持项目级 schedule 配置(不同项目不同频率)
- 使用 node-cron 实现精确到分钟的调度
- systemd service 管理常驻进程(自动重启)
单一备份链路存在单点故障风险。应在多个维度建立冗余:
- 时间周期:高频 + 每日 + 每周
- 存储介质:云存储 + 本地硬盘 + 异地服务器
定期执行实际恢复演练,确认备份可解密、可读取,恢复时间在可接受范围内。
- Linux x64 独立发行包无需预装 Node.js、npm、Bun 或 restic
- npm 安装方式需要 Node.js >= 18
- Linux x64 平台内置 restic 0.18.1,其他平台需自行安装 restic
way 查找 restic 的顺序:
WAY_RESTIC_BIN指定的二进制- Linux x64 npm 包内置的 restic
- Linux x64 独立发行包旁边或安装目录中的 restic
- 系统
PATH中的restic
GitHub Release 提供 Linux x64 独立发行包,安装机无需预装 Node.js、npm、Bun 或 restic:
curl -fsSL https://raw.githubusercontent.com/shellus/way/master/scripts/install.sh | sudo sh默认安装位置:
way:/usr/local/bin/way- 内置 restic:
/usr/local/lib/way/vendor/restic/linux-x64/restic - 示例配置:
/usr/local/lib/way/*.yaml.example
npm install -g @shellus/wayLinux x64 安装方式无需额外安装 restic。如需使用自定义 restic,可设置:
WAY_RESTIC_BIN=/usr/local/bin/restic way restic snapshotsway init编辑 ~/.way/repositories.yaml 填入实际凭证,设置权限:
chmod 600 ~/.way/repositories.yaml# 本地仓库
way restic init
# S3 仓库(指定其他仓库)
way --remote=s3 restic initway systemd install
way systemd statusway backup # 备份所有项目
way backup data # 只备份 data 项目
way restore data --target /tmp/restore --dry-run # 按规则预演恢复
way restic snapshots # 查看快照# 备份命令
way backup # 执行所有项目备份
way backup data # 只备份 data 项目
way backup data config # 备份多个项目
way backup --dry-run # 模拟备份(不实际写入)
way restore data --target /tmp/restore # 按规则恢复 data 项目
way restore data --host old-host --target /tmp/restore --dry-run # 从指定 host 模拟恢复
way restore data --target /tmp/restore --delete # 删除目标中快照不存在的文件
way gc # 按 retention 策略清理旧快照
way gc --dry-run # 模拟清理(不实际删除)
# daemon 模式(推荐)
way daemon # 启动常驻进程,按配置定时执行
# systemd 管理
way systemd install # 安装 systemd service(运行 daemon)
way systemd show # 显示 systemd 配置
way systemd status # 查看服务状态
way systemd uninstall # 卸载服务
# 显式透传 restic(way 只设置环境变量)
way restic snapshots # → restic snapshots
way restic check # → restic check
way restic stats # → restic stats
way restic restore abc123 --target /tmp/restore # → restic restore abc123 --target /tmp/restore
# 指定 repository(默认用 repositories.yaml 中的 default)
way --remote=oss restic snapshotsway backup 会继续执行本轮全部目标项目并输出汇总。全部项目成功时退出码为 0;任一项目失败时退出码为 1。
graph LR
A[way daemon] --> B[读取配置]
B --> C[为每个项目创建 cron 任务]
C --> D[node-cron 定时触发]
D --> E[执行 restic backup]
E --> F[推送 Uptime Kuma]
G[systemd service] --> H[启动 daemon]
H --> I[进程崩溃自动重启]
配置文件默认存放在 ~/.way/,安装后复制示例文件并填入实际值:
cp /usr/local/lib/way/repositories.yaml.example ~/.way/repositories.yaml
cp /usr/local/lib/way/rules.yaml.example ~/.way/rules.yaml也可以通过 WAY_DIR 环境变量指定其他目录:
WAY_DIR=/path/to/config way restic snapshots备份规则配置,参考 rules.yaml.example:
- defaults: 全局默认配置(schedule、retention)
- projects: 备份项目配置,可覆盖默认 schedule 和 retention
- projects.*.hooks: 项目级备份钩子,支持
before_backup和after_backup - uptime_kuma.push_url: 全局 Uptime Kuma Push 地址,作为项目未配置通知地址时的回退
- projects.*.uptime_kuma.push_url: 项目级 Uptime Kuma Push 地址,优先于全局地址
- maintenance: 维护任务配置(prune、check)
- global_excludes: 全局排除规则
项目级钩子用于在 restic 备份前后执行一致性快照、校验或清理脚本:
projects:
data:
paths:
- /path/to/data
hooks:
before_backup:
- run: /path/to/scripts/prepare-data-backup.sh
timeout: "10m"
after_backup:
- run: /path/to/scripts/verify-data-backup.sh
timeout: "5m"before_backup 失败会跳过该项目的 restic 备份并标记项目失败;after_backup 只在 restic 成功后执行,失败同样会标记项目失败。--dry-run 模式只打印钩子命令,不实际执行。钩子命令按 shell 命令执行,并会收到 WAY_PROJECT、WAY_REMOTE、WAY_DIR、WAY_DRY_RUN 环境变量。
Linux 上的 hook 达到 timeout 后,Way 会终止对应 shell 的完整进程组;进程未在宽限期内退出时会继续强制终止,避免数据库导出、压缩或同步子进程脱离 Way 后持续运行。hook 仍应保持幂等,并自行清理失败时生成的临时文件。
项目级 Uptime Kuma 通知示例:
uptime_kuma:
push_url: "https://uptime.example.com/api/push/global-placeholder"
projects:
data:
paths: [/data]
uptime_kuma:
push_url: "https://uptime.example.com/api/push/data-placeholder"
root:
paths: [/root]
uptime_kuma:
push_url: "https://uptime.example.com/api/push/system-placeholder"每个项目优先使用项目级 Push 地址,未配置时回退到全局地址。一次 way backup 中使用相同有效地址的项目只发送一条汇总通知;同组任一项目失败时状态为 DOWN,不同地址的通知互不覆盖。--dry-run 不发送通知,通知请求失败只记录错误,不改变备份结果。
项目级调度示例:
defaults:
schedule: false # 默认不自动调度
projects:
data:
paths: [/data]
schedule: "0 */2 * * *" # 每 2 小时
logs:
paths: [/var/log]
schedule: "0 3 * * *" # 覆盖为每天凌晨 3 点
archive:
paths: [/archive]
schedule: false # 只手动触发:way backup archive
retention:
keep_weekly: 8
keep_monthly: 12手动备份匹配目录示例:
日常备份可排除可重建依赖目录,避免遍历海量小文件:
projects:
data:
paths: [/data]
excludes:
- node_modules
- vendor
schedule: "0 */2 * * *"如果需要偶尔保留依赖目录,可单独配置只手动触发的项目:
projects:
data_deps:
paths: [/data]
include_dirs:
- www/xhj/*/node_modules
- www/xhj/*/vendor
schedule: false手动执行:
way backup data_depsinclude_dirs 是相对 paths 的目录 glob,只支持路径段中的 *,不支持绝对路径和 **。命中目录后,way 会把该目录作为备份根路径写入临时 --files-from 列表。include_dirs 是专用模式,不应用 global_excludes,也不能同时配置项目级 excludes。
schedule 语法(node-cron 格式):
schedule 支持 node-cron 字符串或 false。false 表示不创建自动调度任务,只能通过 way backup <project> 或 way backup 手动触发。项目未设置 schedule 时继承 defaults.schedule;如果全局和项目都未设置,则不自动调度。
| 格式 | 说明 | 示例 |
|---|---|---|
"0 */2 * * *" |
间隔表达式 | 每 2 小时 |
"0 9,15,21 * * *" |
多个时间点(逗号) | 每天 9:00、15:00、21:00 |
"0 9-17 * * 1-5" |
时间范围 | 工作日 9:00-17:00 每小时 |
"*/30 * * * *" |
分钟间隔 | 每 30 分钟 |
false |
禁用自动调度 | 只手动备份 |
restic 使用 Go 的 filepath.Match 语法:
*匹配任意字符,但不跨越目录分隔符**匹配任意子目录
示例:
*Cache*只匹配当前目录下含 Cache 的文件/目录**/*Cache*匹配任意深度子目录下含 Cache 的文件/目录
备份目的地配置,参考 repositories.yaml。
v0.4.0 变更:凭证直接写明文,不再支持 ${VAR} 环境变量语法。
repositories:
local:
type: local
path: /backup/repo
credentials:
password: your-password # 直接明文建议设置文件权限:chmod 600 ~/.way/repositories.yaml
~/.way/ 目录包含所有配置和凭证,建议整体备份到安全位置:
# 打包配置目录
tar czf way-config-$(date +%Y%m%d).tar.gz -C ~ .way
# 加密备份(推荐)
gpg -c way-config-$(date +%Y%m%d).tar.gz如果本机不可访问且遗忘了配置和凭证,所有备份数据将永久无法恢复。
必须在本机之外保存 ~/.way/ 备份:
- 密码管理器(推荐)
- 加密云存储
- 离线存储(U 盘、纸质打印关键凭证)
关键信息:
| 项目 | 位置 |
|---|---|
| 备份规则 | ~/.way/rules.yaml |
| 仓库配置和凭证 | ~/.way/repositories.yaml |
way 提供两种恢复方式:
- 按规则恢复:使用
way restore,根据rules.yaml中的项目路径和way:<project>标签恢复。 - 原始 restic 恢复:使用
way restic restore,显式透传 restic 参数。
way restic snapshots # 列出所有快照
way restic snapshots --tag=way:home # 只看 home 项目的快照
way restic snapshots --tag=way:data # 只看 data 项目的快照way restic ls <snapshot-id> # 列出快照中的所有文件
way restic ls <snapshot-id> /root/.ssh # 列出快照中指定目录的文件
way restic find --snapshot <id> <filename> # 在快照中搜索文件# 恢复 data 项目的最新快照到指定目录
way restore data --target /tmp/restore
# 恢复所有项目
way restore --target /tmp/restore
# 指定快照 ID
way restore data --snapshot abc123 --target /tmp/restore
# 预演恢复,不实际写入
way restore data --host old-host --target /tmp/restore --dry-run
# 让目标目录与快照一致,先 dry-run 核对删除清单
way restore data --host old-host --target /tmp/restore --delete --dry-run
way restore data --host old-host --target /tmp/restore --deleteway restore 会为项目自动添加 --tag=way:<project> 和项目 paths 对应的 --include 参数;传入 --host 时会只恢复指定 host 的快照。
# 显式透传 restic restore
way restic restore <snapshot-id> --target /tmp/restore
# 只恢复特定路径
way restic restore <snapshot-id> --target /tmp/restore --include /root/.ssh
# 恢复单个文件到标准输出(适合快速查看)
way restic dump <snapshot-id> /root/.gitconfigway --remote=oss restic snapshots
way --remote=oss restore data --target /tmp/restore
way --remote=oss restic restore <snapshot-id> --target /tmp/restore参见 CONTRIBUTING.md 了解:
- 项目结构和技术栈
- 开发环境搭建
- 变更规范和测试要求
- 发布流程
项目维护规则:AGENTS.md