起因
家里 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
够用)
踩过的坑
-
第一次全量同步卡 ssh 限速:default OpenSSH 在某些 distro 上有
ChaCha20 加密的 CPU 瓶颈。改[email protected]加密算法
能快 2-3 倍:syncoid --sshcipher [email protected]。 -
远端 dataset 没存在:syncoid 不会自动建 parent dataset。
先手动zfs create backup-pool/nas建好。 -
snapshot 过多:sanoid 没配 retention 时 snapshot 几千个,
zfs list都慢。一定配autoprune = yes。 -
clock skew:源端 / 目标端时间差大于 1 分钟,syncoid 偶尔报
"common ancestor" 找错。两端配 chrony 同 NTP。 -
加密 dataset 同步:源是 encrypted dataset,要加
--sendoptions=w
送 raw encrypted stream,远端不需要密码就能存。
登录后参与评论。