Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Restic Backup Toolkit

A 3-tier macOS backup system: live rsync mirror + restic compressed cold backup + SHA256 integrity audit.
Fully automated via launchd. Zero cloud. Zero subscription. Listed in awesome-restic.

License Platform Language Backup Engine ShellCheck Release Stars Forks

English | 中文文档 | Contributing


Why this exists

macOS has Time Machine, but it's a single-tier, all-or-nothing solution. If you have multiple external drives — a fast work drive, a large archive drive, and an offline cold backup — you need different strategies for each:

  • Work drive → frequent rsync mirror to archive (readable copy, fast recovery)
  • Archive drive → SHA256 integrity check (HDD silent bit rot detection)
  • Cold backup → restic compressed, deduplicated, tiered by importance (disaster recovery)

This toolkit orchestrates all three with launchd automation, status reporting, and monthly audits.

Who is this for?

  • Photographers / videographers with terabytes of irreplaceable media on external HDDs
  • Developers who want their code projects backed up with compression and dedup
  • Data hoarders who already own multiple external drives and want to use them strategically
  • Privacy-conscious users who want zero cloud dependency
  • Anyone who has experienced HDD bit rot and wants to detect it before it's too late

Architecture

┌─────────────────────────────────────────────────────────────┐
│                    3-Tier Backup Strategy                    │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Tier 1: Live Mirror (rsync)                                │
│  ┌──────────────┐    every 30min    ┌──────────────┐       │
│  │  Work Drive   │ ──── (idle) ────→ │ Archive Drive │       │
│  │  (2TB)        │    rsync -a       │ /_WorkMirror  │       │
│  └──────────────┘                   └──────────────┘       │
│                                             │               │
│  Tier 2: Cold Backup (restic)               │               │
│  ┌──────────────┐    on plug-in     ┌──────┴───────┐       │
│  │  Work Drive   │ ──── WatchPaths ─→│ Backup Drive  │       │
│  │  Archive Drive│    (≥6 day throttle) │ (1TB)      │       │
│  └──────────────┘    restic backup  └──────────────┘       │
│                      --compression max                      │
│                      tiered by priority                     │
│                                                             │
│  Tier 3: Integrity Audit                                    │
│  ┌──────────────┐    monthly         ┌──────────────┐       │
│  │ Archive Drive │ ─── shasum -c ───→ │ .sha256       │       │
│  │               │    verify          │ manifests     │       │
│  └──────────────┘                   └──────────────┘       │
│                                                             │
│  + Monthly Audit: checks all tiers, sends notification      │
│    if any tier has issues                                   │
└─────────────────────────────────────────────────────────────┘

What's included

Script Purpose Trigger
daily-work-mirror.sh rsync work content to archive (readable mirror) launchd every 30min + idle ≥30min
restic-cold-1tb.sh Tiered restic compressed backup to cold drive launchd WatchPaths (on plug-in), ≥6 day throttle
restic-prune.sh Prune old snapshots (keep 7 daily / 4 weekly / 12 monthly) launchd daily
restic-check.sh Repository integrity check (read-data-subset 1%) launchd monthly
shasum-verify.sh SHA256 integrity verification of archive files launchd monthly
backup-monthly-audit.sh Audit all tiers, send notification if issues found launchd monthly
rsync-media-mirror.sh Standalone media mirror (optional, for media libraries) manual or custom launchd
install.sh One-click install (registers all launchd jobs) manual
uninstall.sh Uninstall all launchd jobs manual

Quick Start

# 1. Clone
git clone https://github.com/andersyin/restic-backup-toolkit.git
cd restic-backup-toolkit

# 2. Install restic
brew install restic

# 3. Store repository password in Keychain (do not export RESTIC_PASSWORD)
security add-generic-password -s restic-backup-password -a restic -w 'your-password'

# 4. Edit config.sh — replace the YourWorkDrive / YourArchiveDrive / YourBackupDrive placeholders
nano config.sh

# 5. Install (refuses placeholder paths; inits restic repo if the cold drive is mounted; registers launchd jobs)
bash install.sh

# 6. First run (recommended)
bash shasum-verify.sh gen
DAILY_WORK_MIRROR_FORCE=1 bash daily-work-mirror.sh
# Plug in the cold drive, then:
RESTIC_COLD_FORCE=1 bash restic-cold-1tb.sh

install.sh copies the plist files from this directory into ~/Library/LaunchAgents, substituting __SCRIPT_DIR__ and volume paths. uninstall.sh unloads those agents and removes status/log files; it does not delete restic repositories, mirrors, or the Keychain item.

Configuration (config.sh)

All paths and settings are in one file:

# Drive paths
WORK_VOLUME="/Volumes/YourWorkDrive"
ARCHIVE_VOLUME="/Volumes/YourArchiveDrive"
COLD_VOLUME="/Volumes/YourBackupDrive"

# Restic repo
COLD_REPO="$COLD_VOLUME/restic-cold"

# What to back up (tiered by importance)
COLD_TIER1_DIRS=("$WORK_VOLUME/Knowledge Base")           # Critical work
COLD_TIER2_DIRS=("$ARCHIVE_VOLUME/iPhone_Videos")         # Irreplaceable media
COLD_TIER3_DIRS=("$ARCHIVE_VOLUME/视频")                   # Other archives

# What to mirror
WORK_MIRROR_DIRS=("$WORK_VOLUME/Knowledge Base")

# Restic binary (Apple Silicon default; Intel `/usr/local/bin/restic` is auto-detected)
RESTIC="/opt/homebrew/bin/restic"

# Capacity thresholds
STOP_PCT=92        # Stop cold backup if drive exceeds 92% full

How it works

Tier 1: Live Mirror (rsync)

daily-work-mirror.sh runs every 30 minutes via launchd, but only executes when the system has been idle for ≥30 minutes (or during 23:00-06:00 night window). Uses rsync -a without --delete for space safety — deleted files on the source are preserved in the mirror.

Tier 2: Cold Backup (restic)

restic-cold-1tb.sh triggers when the cold backup drive is plugged in (via launchd WatchPaths), but only runs if ≥6 days since last successful backup (configurable). Backs up in priority tiers:

  1. T1 — Critical work (small, must back up)
  2. T2 — Irreplaceable personal media (photos, videos)
  3. T3 — Other archives (only if space allows)

Each tier checks disk usage before starting. If the drive exceeds STOP_PCT, remaining tiers are skipped. Uses restic backup --compression max for maximum space efficiency.

Tier 3: Integrity Audit

shasum-verify.sh generates and verifies SHA256 manifests of archive directories. HDDs can silently corrupt data (bit rot); this is the only way to detect it without cloud redundancy.

Generate manifests (after adding new files to archive):

bash shasum-verify.sh gen

Verify (monthly, automated):

bash shasum-verify.sh verify

Monthly Audit

backup-monthly-audit.sh runs on the 2nd of each month and checks:

  1. Did restic-check pass? (repository integrity)
  2. Did shasum-verify pass? (archive integrity)
  3. Are there enough SHA256 manifests?
  4. Are there enough restic snapshots?

Sends a macOS notification only if something is wrong. Silent on success.

Real-world output

What it looks like when the scripts run:

$ bash restic-cold-1tb.sh

=== Cold Backup (restic) — 2026-08-12 14:30 ===
Drive usage: 68% (threshold: 92%)

[T1-Work] Backing up: /Volumes/Work/Knowledge Base
  creating new snapshot in repo ...
  [0:00:23] 1.2 GiB added  (15.4 GiB stored)
  snapshot 8a3f2c1d saved

[T2-Personal] Backing up: /Volumes/Archive/iPhone_Videos
  [0:01:47] 3.8 GiB added  (19.2 GiB stored)
  snapshot b7e4a9f2 saved

[T3-Archive] Backing up: /Volumes/Archive/视频
  Drive at 89%, continuing...
  [0:03:12] 5.1 GiB added  (24.3 GiB stored)
  snapshot c1d8e5a3 saved

=== Done: 3 snapshots, 10.1 GiB added ===
$ cat /tmp/restic-cold-1tb.status

{"status":"ok","tiers_run":3,"snapshots":3,"gb_added":"10.1","ts":"2026-08-12T14:33:51Z"}
$ bash shasum-verify.sh verify

[2026-08-12 10:00:01] VERIFY iPhone_Videos ...
  4,832 files checked — OK
[2026-08-12 10:02:15] VERIFY iPhone_Photos ...
  12,047 files checked — OK
[2026-08-12 10:05:33] VERIFY 视频 ...
  856 files checked — OK
[2026-08-12 10:05:48] All directories verified: 3/3 OK

FAQ

Why not just use Time Machine?

Time Machine is great for system-level recovery, but it's single-tier: one backup drive, no compression, no dedup, no bit rot detection, and no tiered priority. If your backup drive fills up, Time Machine silently deletes old backups. This toolkit gives you three independent layers of protection, each with a different failure mode.

Why not just use Backblaze / cloud backup?

Three reasons: (1) cost — uploading terabytes of data costs money every month forever; (2) speed — restoring from cloud is hours or days, rsync mirror is instant; (3) privacy — your data never leaves your physical drives. This toolkit is designed for people who already own multiple external drives.

Do I need exactly 3 drives?

No. The minimum is 2 drives (work + backup). The 3-tier setup (work + archive + cold backup) is the recommended configuration. You can also use the scripts independently — for example, just shasum-verify.sh for bit rot detection on a single archive drive.

What happens if a drive fails mid-backup?

Each tier is independent. If the cold backup drive fails during T2, T1 is already backed up. rsync mirror doesn't use --delete, so source data is never at risk. restic snapshots are atomic — a failed backup leaves the repository in a consistent state.

How much disk space does restic save vs rsync?

Typically 40-60% less, due to compression (--compression max) and deduplication. If you back up overlapping directories (e.g., work files that also appear in the archive), restic deduplicates them automatically.

Status Files

All scripts write JSON status to /tmp/:

cat /tmp/daily-work-mirror.status    # mirror status
cat /tmp/restic-cold-1tb.status      # cold backup status
cat /tmp/restic-prune.status         # prune status
cat /tmp/restic-check.status         # check status
cat /tmp/shasum-verify.status        # shasum status
cat /tmp/backup-monthly-audit.status # monthly audit summary

Logs

cat /tmp/daily-work-mirror.log       # mirror log
cat /tmp/restic-cold-1tb.log         # cold backup log
cat /tmp/restic-backup.log           # prune/check log
cat /tmp/shasum-verify.log           # shasum log
cat /tmp/backup-monthly-audit.log    # audit log

File Structure

restic-backup-toolkit/
├── config.sh                              # All configuration (edit this)
├── daily-work-mirror.sh                   # rsync work mirror (Tier 1)
├── restic-cold-1tb.sh                     # restic cold backup (Tier 2)
├── restic-prune.sh                        # Snapshot pruning
├── restic-check.sh                        # Repository integrity check
├── shasum-verify.sh                       # Archive SHA256 verification (Tier 3)
├── backup-monthly-audit.sh                # Monthly audit of all tiers
├── rsync-media-mirror.sh                  # Optional standalone media mirror
├── install.sh                             # One-click install
├── uninstall.sh                           # Uninstall launchd jobs (keeps backup data)
├── com.user.daily-work-mirror.plist       # launchd: mirror every 30min
├── com.user.restic-cold-1tb.plist          # launchd: cold backup on plug-in
├── com.user.restic-prune.plist             # launchd: prune daily
├── com.user.restic-check.plist             # launchd: check monthly
├── com.user.shasum-verify.plist            # launchd: shasum monthly
├── com.user.backup-monthly-audit.plist     # launchd: audit monthly
├── CONTRIBUTING.md
├── LICENSE
└── .github/workflows/shellcheck.yml       # CI: bash -n + ShellCheck

Requirements

  • macOS with Homebrew
  • restic (brew install restic) — Apple Silicon and Intel paths are detected automatically
  • Python 3 (pre-installed on recent macOS, or Xcode Command Line Tools)
  • 2+ external drives (work + backup)
  • /bin/bash needs Full Disk Access so launchd can read Keychain (System Settings → Privacy & Security → Full Disk Access)

Scripts pass the restic password with RESTIC_PASSWORD_COMMAND (Keychain). They do not export RESTIC_PASSWORD, which can leak via process environment listings.

Comparison

Feature Time Machine Carbon Copy Cloner Backblaze This toolkit
Free Yes (Mac only) $40 $9/mo Yes
Multi-tier No Partial No Yes (3 tiers)
Compression No No Yes Yes (restic max)
Dedup No No Yes Yes (restic)
Bit rot detection No No No Yes (SHA256)
Offline backup No Yes No (cloud) Yes
Readable mirror Yes Yes No Yes (rsync)
Zero cloud No Yes No Yes
Open source No No No Yes

Uninstall

bash uninstall.sh

Removes all launchd jobs and status files. Backup data and restic repositories are preserved. The Keychain password is left in place; delete it yourself if you want:

security delete-generic-password -s restic-backup-password -a restic

License

MIT


中文文档

macOS 三盘备份脚本集:rsync 实时镜像 + restic 压缩冷备 + SHA256 完整性巡检。全程 launchd 自动化,零云依赖。

三盘分层

层级 工具 触发 说明
实时镜像 rsync 每 30 分钟 + 空闲 ≥30 分钟 工作内容 → 档案盘可读副本
离线冷备 restic 插盘自动,≥6 天节流 按重要性分层压缩灾备
完整性巡检 shasum 每月 检测 HDD 静默坏道
月度审计 每月 汇总检查三层状态,异常才通知

快速开始

git clone https://github.com/andersyin/restic-backup-toolkit.git
cd restic-backup-toolkit
brew install restic

# 存密码到 Keychain(不要 export RESTIC_PASSWORD)
security add-generic-password -s restic-backup-password -a restic -w '你的密码'

# 改配置(必须替换 YourWorkDrive 等占位路径,否则 install.sh 会拒绝安装)
nano config.sh

# 一键安装(plist 从本目录复制到 ~/Library/LaunchAgents)
bash install.sh

# 首次运行
bash shasum-verify.sh gen
DAILY_WORK_MIRROR_FORCE=1 bash daily-work-mirror.sh
RESTIC_COLD_FORCE=1 bash restic-cold-1tb.sh

配置说明

所有路径和参数集中在 config.sh

  • WORK_VOLUME / ARCHIVE_VOLUME / COLD_VOLUME — 三个磁盘卷路径
  • COLD_TIER1_DIRS / COLD_TIER2_DIRS / COLD_TIER3_DIRS — 冷备分层目录
  • WORK_MIRROR_DIRS — 工作镜像目录
  • SHASUM_DIRS — SHA256 巡检目录
  • STOP_PCT / MIN_DAYS — 容量阈值和周期节流

注意事项

  • /bin/bash 需要在「系统设置 → 隐私与安全性 → 完全磁盘访问权限」中开启,否则 launchd 上下文无法读 Keychain
  • restic 路径会自动检测 Apple Silicon (/opt/homebrew/bin/restic) 与 Intel (/usr/local/bin/restic)
  • 首次使用 shasum-verify.sh 需要先 bash shasum-verify.sh gen 生成清单
  • uninstall.sh 只移除 launchd 任务和日志,不删除冷仓、镜像和 Keychain 密码

You might also like

  • iphone-auto-backup — Auto-backup iPhone photos & videos on USB plug-in, EXIF date archiving, content-hash dedup
  • md-reader — Zero-dependency single-file HTML markdown reader, 77KB, no install

Star History

If this toolkit saved your data, consider giving it a ⭐

Report a bug · Request a feature · Contributing

About

3-tier macOS backup: rsync mirror + restic cold backup + SHA256 audit. launchd automation, zero cloud.

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages