用 syncoid 把 ZFS 数据集自动增量同步到异地(家用 NAS → VPS)

起因

家里 NAS 上有 4TB 照片 + 项目数据,本地做了 ZFS RAID1(同一台机器
两块盘镜像)。但火灾 / 水患 / 误删 ZFS 仓库 → 一晚上全没。
"异地备份"是真灾备。

需求:每天把 NAS 上某个 dataset 增量同步到一台远程 VPS(10GB 起价
便宜的 storage VPS)。要求:增量、加密传输、自动管理 snapshot 链、
失败告警。

手写 zfs send | ssh ... | zfs receive 能行,但要自己管"上一次成功
的 base snapshot 是哪个"非常烦。syncoid(sanoid 项目的一部分)自动
处理整套流程。

解决方案

NAS + VPS 都要装:

# Debian / Ubuntu
sudo apt install sanoid

syncoid --version

配 SSH 密钥(NAS → VPS 单向)

NAS:

sudo ssh-keygen -t ed25519 -f /root/.ssh/syncoid -C 'syncoid'
sudo cat /root/.ssh/syncoid.pub

VPS:

sudo useradd -m -s /bin/bash zfsbackup
sudo mkdir -p /home/zfsbackup/.ssh
sudo tee -a /home/zfsbackup/.ssh/authorized_keys <<'EOF'
command="zfs receive *",no-pty,no-port-forwarding ssh-ed25519 AAAA...
EOF
sudo chown -R zfsbackup:zfsbackup /home/zfsbackup/.ssh
sudo chmod 700 /home/zfsbackup/.ssh
sudo chmod 600 /home/zfsbackup/.ssh/authorized_keys

command="zfs receive *" 限定这把 key 只能跑 zfs receive,
即使泄露也不能登 shell。

远程仓库

VPS 建 ZFS pool(数据盘,比如 /dev/sdb):

sudo zpool create -O compression=zstd backup-pool /dev/sdb
sudo zfs create backup-pool/nas

sudo zfs allow zfsbackup create,mount,receive,destroy,snapshot \
  backup-pool/nas

zfs allow 让 zfsbackup 用户能 receive 到这个 dataset 下。

测试一次同步

NAS:

sudo syncoid -i /root/.ssh/syncoid \
  tank/photos [email protected]:backup-pool/nas/photos

第一次跑会全量传 4TB(取决带宽,几小时-几天)。后续增量只传差异。

syncoid 自动:

  • 在源端创建一个 syncoid_* 快照
  • zfs send -i <last-common> <new> 增量发到远端
  • 远端 zfs receive
  • 删除源端不再需要的老 syncoid 快照(保留最近两个用于下次增量)

sanoid 管本地 snapshot retention

NAS /etc/sanoid/sanoid.conf

[tank/photos]
    use_template = production
    recursive = yes

[template_production]
    hourly = 36
    daily = 30
    monthly = 12
    yearly = 3
    autosnap = yes
    autoprune = yes

cron / timer 跑:

sudo systemctl enable --now sanoid.timer
# 默认每 15 分钟检查

每小时打 snapshot,保留 36 小时;每天保留 30 天;每月保留 12 个月。
本地 snapshot 给"刚才误删了文件,立刻回来"用;syncoid 异地同步给"机器
没了"用。

自动化 syncoid

/etc/systemd/system/syncoid-nas.service

[Unit]
Description=ZFS replicate tank/photos to vps
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/sbin/syncoid \
  --quiet \
  -i /root/.ssh/syncoid \
  tank/photos \
  [email protected]:backup-pool/nas/photos
ExecStartPost=-/usr/local/sbin/notify-success.sh syncoid
OnFailure=[email protected]

/etc/systemd/system/syncoid-nas.timer

[Timer]
OnCalendar=*-*-* 02:30:00
RandomizedDelaySec=30m
Persistent=true
[Install]
WantedBy=timers.target
sudo systemctl enable --now syncoid-nas.timer

校验异地数据

VPS:

sudo zfs list -t snapshot backup-pool/nas/photos | tail -5
# 应当看到最近 syncoid_* snapshot

sudo zpool scrub backup-pool   # 每月一次校验

还原演练(重要)

最佳实践:每季度做一次"模拟 NAS 没了"演练。
从 VPS 拉回最近 snapshot:

ssh nas
sudo zfs send -R zfsbackup@vps:backup-pool/nas/photos@syncoid_2026_05_20 \
  | sudo zfs receive -F tank/photos-restored

确认能用 + 文件完整 + 权限对。演练过的备份才是真备份

效果

  • 4TB 数据每天增量同步,平均增量 100-500 MB(取决于使用量)
  • 上传跑半小时-1 小时(看带宽),完全在低峰期 02:30
  • 本地误删 → ZFS snapshot 秒级恢复
  • NAS 全挂 → VPS 拉回完整数据,零数据丢失
  • VPS 端 zstd 压缩让 4TB 实际占 ~2.4 TB(10 美元 / 月的 storage VPS
    够用)

踩过的坑

  1. 第一次全量同步卡 ssh 限速:default OpenSSH 在某些 distro 上有
    ChaCha20 加密的 CPU 瓶颈。改 [email protected] 加密算法
    能快 2-3 倍:syncoid --sshcipher [email protected]

  2. 远端 dataset 没存在:syncoid 不会自动建 parent dataset。
    先手动 zfs create backup-pool/nas 建好。

  3. snapshot 过多:sanoid 没配 retention 时 snapshot 几千个,
    zfs list 都慢。一定配 autoprune = yes

  4. clock skew:源端 / 目标端时间差大于 1 分钟,syncoid 偶尔报
    "common ancestor" 找错。两端配 chrony 同 NTP。

  5. 加密 dataset 同步:源是 encrypted dataset,要加 --sendoptions=w
    送 raw encrypted stream,远端不需要密码就能存。

精确评价 共 0 人评价
可复现性
可复现 · 0 不可复现 · 0
文风
文风流畅 · 0 文风晦涩 · 0
立场
支持 · 0 反对 · 0

登录后即可对本帖作出评价。

评论区 0 条 · 所有人可在此交流

登录后参与评论。

还没有评论,来说两句。