Снапшоты Btrfs защищают от неудачного обновления, но лежат на том же диске. Если диск выйдет из строя, исчезнут и данные, и копии. Btrfs send/receive переносит снапшоты на другой носитель и умеет передавать только изменения — это дешёвый инкрементальный бэкап без сторонних агентов.
Как работает передача
btrfs send читает снапшот и выдаёт поток изменений в стандартный вывод. btrfs receive применяет поток на другой файловой системе и создаёт subvolume. Передавать можно только снапшоты, доступные только для чтения: иначе содержимое изменится прямо во время чтения.
Инкрементальный режим указывает родительский снапшот через -p. Принимающая сторона должна уже иметь этот родительский subvolume с тем же идентификатором. Если родителя нет, поток не применится — это главная причина ошибок при восстановлении на новом диске.
Подготовка и первый снапшот
Сначала проверьте, что источник и приёмник — Btrfs, а на приёмнике достаточно места. Снапшоты удобно складывать в отдельный subvolume, чтобы они не попадали в собственный бэкап.
btrfs subvolume list /
btrfs filesystem usage /mnt/backup
mkdir -p /home/.snapshots
btrfs subvolume snapshot -r /home /home/.snapshots/base-2026-11-06
Флаг -r делает снапшот доступным только для чтения. Проверить это можно через btrfs property get -ts. Дата в имени помогает строить цепочки, но для скриптов надёжнее отдельный файл состояния с именем последнего успешного снапшота.
btrfs property get -ts /home/.snapshots/base-2026-11-06 ro
btrfs subvolume show /home/.snapshots/base-2026-11-06 | head -20
Полная копия
Первый поток содержит все данные и может быть большим. Его отправляют на приёмник один раз, а дальше строят цепочку от него.
btrfs send /home/.snapshots/base-2026-11-06 | btrfs receive /mnt/backup
btrfs subvolume list /mnt/backup
du -sh /mnt/backup/base-2026-11-06
Приёмник создаёт каталог с тем же именем, что и снапшот. Проверьте, что subvolume виден и доступен для чтения. Если места мало, используйте сжатие потока: zstd даёт хороший компромисс скорости и степени сжатия. Сжатие на лету экономит диск и сеть, но нагружает процессор.
btrfs send /home/.snapshots/base-2026-11-06 | zstd -T0 -3 > /mnt/backup/base.zst
zstd -d -c /mnt/backup/base.zst | btrfs receive /mnt/backup
Так получается сжатый архив, который можно хранить отдельно и распаковать позже. При прямой передаче между процессами поток нельзя сжимать без распаковки на приёмнике: btrfs receive не понимает zstd и получит мусор. Для сети используйте схему с zstd -d на удалённой стороне, она показана ниже.
Инкрементальная передача
Второй и последующие снапшоты передаются с указанием родителя. Поток содержит только изменённые блоки, поэтому занимает меньше места и времени.
btrfs subvolume snapshot -r /home /home/.snapshots/2026-11-07
btrfs send -p /home/.snapshots/base-2026-11-06 /home/.snapshots/2026-11-07 | btrfs receive /mnt/backup
btrfs subvolume list -r /mnt/backup
Родитель должен существовать на обеих сторонах. Если вы удалили старый снапшот на источнике, следующая инкрементальная передача от него не построится. Поэтому цепочку планируют заранее: либо хранят базу долго, либо периодически делают новый полный снапшот и начинают цепочку заново.
Проверить, что приёмник получил именно инкремент, можно по размеру и по списку subvolume. Полный поток от базы каждый раз — не ошибка, но он снова передаст все данные.
Скрипт с ротацией
Ручные команды быстро забываются, поэтому передачу оформляют скриптом с блокировкой от параллельного запуска. Ниже — минимальный вариант без внешних зависимостей.
#!/usr/bin/env bash
set -euo pipefail
SRC=/home
DST=/mnt/backup
SNAP="$SRC/.snapshots/$(date +%F-%H%M)"
LAST=$(cat /var/lib/btrfs-last 2>/dev/null || true)
btrfs subvolume snapshot -r "$SRC" "$SNAP"
if [ -n "$LAST" ] && btrfs subvolume list "$DST" | grep -q "$(basename "$LAST")"; then
btrfs send -p "$LAST" "$SNAP" | btrfs receive "$DST"
else
btrfs send "$SNAP" | btrfs receive "$DST"
fi
echo "$SNAP" > /var/lib/btrfs-last
Скрипт обновляет файл состояния только после успешной передачи. Если receive упал, следующая попытка снова возьмёт прежнего родителя. Ротацию делайте отдельной задачей: удаляйте старые снапшоты и на источнике, и на приёмнике, но никогда не удаляйте родителя, на который ещё ссылается цепочка.
Передача по SSH
Сетевой вариант почти не отличается: поток идёт через ssh, а распаковка выполняется на приёмнике. Для регулярных задач настройте ключ без пароля и ограничьте команду в authorized_keys.
btrfs send -p "$LAST" "$SNAP" | zstd -T0 -3 | ssh backup@nas 'zstd -d | btrfs receive /srv/backup'
ssh backup@nas 'btrfs subvolume list /srv/backup'
Ограничение команды через command=»…» в authorized_keys снижает риск: даже при утере ключа злоумышленник не получит полноценный shell. Проверьте, что на приёмнике есть место и что каталог смонтирован. Разрывы соединения обрывают поток, поэтому для больших объёмов лучше запускать передачу в tmux или systemd-юните с повторной попыткой.
Проверка и восстановление
Бэкап считается рабочим только после проверки. Сравните список файлов и выборочно прочитайте данные. Для восстановления достаточно создать снапшот из принятого subvolume или скопировать файлы.
btrfs subvolume snapshot /mnt/backup/2026-11-07 /mnt/restore/home
diff -r /home/Documents /mnt/restore/home/Documents | head
btrfs filesystem du -s /mnt/backup/* | tail -5
btrfs scrub start -B /mnt/backup
Scrub проверяет контрольные суммы и может выявить повреждение на приёмнике. Для важных данных держите вторую копию на другом носителе: send/receive защищает от логических ошибок и отказа диска, но не от пожара в комнате. Восстановление на другой компьютер стоит протестировать заранее.
Ограничения
Поток send/receive не заменяет файловый бэкап для других файловых систем и не переносит настройки монтирования. Он также не сжимает поток сам по себе: сжатие делает внешний инструмент вроде zstd. Нельзя принимать поток на XFS или ext4 — приёмник обязан быть Btrfs.
Ещё одна тонкость: инкремент опирается на родителя, а не на «любой похожий» снапшот. Переименование или удаление родителя ломает цепочку. Ведите журнал снапшотов и проверяйте его при каждой ротации. Для больших массивов полезно считать время передачи: если скрипт перешёл к полной отправке, проверьте цепочку снапшотов.
Чек-лист
- Источник и приёмник — Btrfs, места достаточно.
- Снапшоты создаются только для чтения.
- Родительский снапшот есть на обеих сторонах.
- Скрипт обновляет состояние только после успеха.
- Ключ SSH ограничен командой receive.
- Восстановление и scrub проверены на практике.
Итог
Btrfs send/receive даёт предсказуемый инкрементальный бэкап без лишних пакетов: снапшот, поток, приёмник. Секрет надёжности — дисциплина цепочки: не удалять родителя, фиксировать состояние после успешной передачи и регулярно проверять восстановление. Добавьте второй носитель и scrub, и копия перестанет быть формальностью.