Снапшоты ZFS спасают от неаккуратного удаления файлов и от сбойного обновления, но не спасают от смерти носителя: если диск умер вместе с машиной, локальных копий больше нет. В домашней лаборатории логична схема из двух узлов: основной мини-ПК с пулом и старая машина с одним диском в роли приёмника. Настройка репликации сводится к паре команд и одному скрипту по расписанию — пройдём по шагам.
Создание пула
Имена дисков в /dev/sdX недолговечны и меняются после перезагрузки, поэтому с самого начала работайте с символьными ссылками по идентификаторам:
ls -l /dev/disk/by-id/
sudo zpool create -o ashift=12 -O compression=zstd -O atime=off -O mountpoint=/tank tank mirror /dev/disk/by-id/ata-DISK1 /dev/disk/by-id/ata-DISK2
sudo zpool status tank
sudo zfs list
Зеркало из двух дисков — разумный минимум для домашних данных. Параметр ashift=12 соответствует секторам 4 КБ современных накопителей, сжатие zstd почти не грузит процессор, а отключённый atime избавляет от лишних записей при каждом чтении. Отдельным датасетом создайте каталог с данными, чтобы не монтировать файлы прямо в корне пула:
sudo zfs create tank/data
sudo zfs set sharenfs=on tank/data
Снапшоты
Снапшот — мгновенный снимок состояния датасета, который занимает место только под изменённые блоки:
sudo zfs snapshot tank/data@clean
sudo zfs list -t snapshot -o name,creation,used tank/data
Для дерева датасетов снимайте рекурсивно: zfs snapshot -r tank/data@before-update. Откат выполняется через rollback, но учтите: он удаляет все снапшоты, сделанные после целевого. Если старая версия нужна только для сравнения, надёжнее сделать клон командой zfs clone и скопировать нужные файлы руками — так ничего не потеряется. Практичные имена для регулярных снимков:
- @clean — точка отсчёта перед ручным изменением конфигурации или обновлением;
- @before-update — снимок непосредственно перед установкой пакетов;
- @daily-2026-11-05 — автоматический снимок от скрипта по расписанию;
- @last-sent — последний снимок, ушедший на резервную машину.
Первый полный перенос
На приёмной машине должен быть установлен zfsutils-linux, а доступ по SSH — по ключу без запроса пароля:
ssh backup "zpool list"
sudo zfs send tank/data@clean | ssh backup "zfs recv -F backup/tank/data"
ssh backup "zfs list backup/tank/data"
Полный поток содержит все данные датасета, поэтому в первый раз он самый тяжёлый и занимает время пропорционально объёму. На приёмнике данные попадают в пул backup — держите резервный пул отдельным, чтобы не смешивать его с системными датасетами. Флаг -F разрешает перезапись приёмной части, что необходимо для повторных приёмов поверх существующих данных.
Инкрементальная репликация
Дальше переносятся только изменённые блоки между двумя снапшотами:
sudo zfs send -c -i tank/data@clean tank/data@next | ssh backup "zfs recv -F backup/tank/data"
Ключ -c включает сжатие потока на лету, а -i задаёт отправку инкремента от предыдущего снапшота. Для дерева датасетов добавляют -R, тогда команда переносит структуру и свойства целиком. Ограничение одно: инкремент требует, чтобы родительский снапшот существовал на приёмнике. Если вы удалили его скриптом чистки, перенос упадёт с ошибкой, и придётся заново делать полный send.
Если перенос прервался
Большой поток легко оборвать: пропало соединение по SSH или закончилось место на приёмнике. Решается возобновляемой отправкой: приёмник сохраняет состояние, а источник докачивает остаток по токену.
sudo zfs send -c -i tank/data@clean tank/data@next | ssh backup "sudo zfs recv -s -F backup/tank/data"
ssh backup "zfs get -H -o value receive_resume_token backup/tank/data"
sudo zfs send -t ТОКЕН_ИЗ_ВЫВОДА
Приём с флагом -s записывает токен продолжения в свойство receive_resume_token. Выведите это значение, подставьте в send -t — и останется докачать только оставшуюся часть потока. На медленном канале между комнатами эта функция экономит часы и нервы.
Доступ по SSH только для репликации
Скрипту нужен негоняемый доступ к приёмнику. Заведите отдельную пару ключей без парольной фразы и запишите публичный ключ в файл /root/.ssh/authorized_keys на резервной машине:
ssh-keygen -t ed25519 -f ~/.ssh/zfs_repl -N ""
# строка в authorized_keys приёмной машины:
restrict,from="192.168.1.10" ssh-ed25519 AAAA... zfs-repl
Ключевое слово restrict снимает все привилегии ключа, оставляя перенаправление потока, а аргумент from привязывает его к адресу основной машины. Даже утечка ключа не даст с него залогиниться в интерактивную сессию.
Автоматизация по расписанию
Ручные команды хороши для проверки, но репликация должна идти сама. Пример скрипта, который помнит имя последнего отправленного снапшота:
#!/bin/sh
set -e
LAST=$(sudo zfs list -H -t snapshot -s creation -o name tank/data | tail -1)
PREV=$(cat /var/lib/zfs-repl/last 2> /dev/null || true)
if [ -n "$PREV" ] && sudo zfs list -H -t snapshot "$PREV" > /dev/null 2>&1; then
sudo zfs send -c -i "$PREV" "$LAST" | ssh backup "zfs recv -F backup/tank/data"
else
sudo zfs send "$LAST" | ssh backup "zfs recv -F backup/tank/data"
fi
echo "$LAST" > /var/lib/zfs-repl/last
Логика проста: если предыдущий снапшот ещё существует на источнике — отправляем инкремент, иначе — полный поток. Повесьте скрипт на systemd-таймер или cron, например раз в сутки, и заведите отдельную SSH-ключевую парь только для репликации, чтобы её нельзя было использовать для интерактивного входа.
Снапшоты на приёмнике и скраб
Принятые снапшоты никуда не деваются и растут со временем. Посмотрите на них и приберитесь:
ssh backup "zfs list -t snapshot -r backup/tank/data"
ssh backup "sudo zfs destroy backup/tank/data@old"
sudo zpool scrub tank
ssh backup "sudo zpool scrub backup"
Скраб сверяет контрольные суммы всех блоков и находит деградацию до того, как о ней сообщат пользователи. Раз в месяц на источнике и раз в квартал на приёмнике — достаточный ритм для домашней лаборатории. Следите и за местом: zpool list показывает используемый объём, а на приёмнике он растёт ровно настолько, насколько отстаёт резервная копия от источника. Результат проверяется командой zpool status: строка об окончании скраба без ошибок означает, что данные целы на обоих узлах.
Итог
Надёжность домашнего хранилища складывается из трёх элементов: регулярных снапшотов на источнике, инкрементальной репликации на отдельную машину и периодического скраба обоих пулов. Снапшот защищает от ошибок, репликация — от потери носителя, скраб — от тихой порчи данных. Настроив скрипт один раз, вы получаете почти незаметную, но проверяемую схему резервного копирования, которая не требует участия каждый день.