BorgBackup: инкрементальный бэкап с шифрованием и автоматизацией через systemd

borgbackup инкрементальный бэкап

Быстрый ответ. BorgBackup — консольная утилита для инкрементальных бэкапов с дедупликацией и шифрованием на клиенте. Ставится через apt/pip, инициализация репозитория — одна команда, автоматизация — через systemd-timer. Первый архив весит как полный бэкап, все последующие — только дельта. Восстановление — через borg extract или монтирование архива как обычной папки.

Суть - если нет времени читать всё
Установи Borg. Инициализируй репозиторий с шифрованием repokey-blake2. Настрой systemd-timer на ежедневный запуск borg create. Добавь borg prune с политикой хранения 7 daily / 4 weekly / 6 monthly. Раз в месяц проверяй восстановление руками, не доверяй логам вслепую.

1. Диагноз: почему rsync и tar уже не то

Поднял продакшн-сервер. Настроил cron с tar раз в ночь. Через полгода диск с бэкапами забит под завязку, потому что каждую ночь пишется полный архив заново. Знакомо? У большинства так и есть — до первого раза, когда бэкап понадобился реально, а места под него не осталось.

BorgBackup решает это дедупликацией на уровне чанков: система разбивает файлы на переменные блоки, хеширует и хранит только уникальные куски. Инкрементальный бэкап BorgBackup после первого полного архива занимает столько места, сколько реально изменилось — и не байтом больше.

Что получишь на выходе этой статьи:

  • Рабочий зашифрованный репозиторий Borg
  • Автоматический запуск через systemd-timer без cron
  • Политику ротации, которая не жрёт диск бесконечно
  • Проверенную процедуру восстановления, а не веру на слово

Время на настройку — 30-40 минут, если не тупить в документацию. Нужен доступ по SSH с правами на запись в целевую директорию и python3 актуальной версии.

Системные требования

Компонент Минимальная версия Рекомендуется
ОС Debian 11 / Ubuntu 20.04 Debian 12 / Ubuntu 24.04
Python 3.9 3.10+
BorgBackup 1.2.x 1.4.x (стабильная ветка)
OpenSSH (для remote-репозитория) 7.x 9.x

На момент публикации актуальна стабильная версия 1.4.5 (ветка 1.4). Ветка 2.0 пока в бете — не тащи её в продакшн, сколько бы ни хотелось новых плюшек. Перед установкой проверь свежие релизы на официальном сайте borgbackup.org.

2. Причины бежать от tar/rsync к Borg

Причина Почему это ломает работу без Borg
Полные копии каждую ночь Диск с бэкапами растёт линейно, место кончается внезапно и в самый неподходящий момент
Нет шифрования из коробки tar + удалённое хранилище = данные лежат открытым текстом на чужом сервере
Нет проверки целостности Битый бэкап обнаруживается только в момент восстановления — то есть когда уже поздно
Ручная ротация через find -mtime Один неверный флаг — и скрипт удаляет не то, что нужно
Нет дедупликации между машинами Бэкапим 10 однотипных серверов — платим за 10x место вместо разумного
Долгое восстановление отдельного файла Приходится распаковывать весь архив, чтобы достать один конфиг

Капля никотина убивает лошадь. Одна строка в crontab без комментария — весь отдел, который потом два часа гадает, что за скрипт стирает бэкапы недельной давности.

3. Рецепт: установка и настройка Borg

Подготовка

Проверь версию Python и наличие пакетного менеджера. Borg тянет за собой не так много зависимостей, но лучше свериться заранее.


python3 --version
apt list --installed 2>/dev/null | grep -i openssh

Результат: python3 версии не ниже 3.9, ssh-клиент установлен.

Шаг 1. Установка BorgBackup

Два пути — через системный пакет или через pip. Системный пакет проще, pip даёт более свежую версию.


sudo apt update
sudo apt install -y borgbackup
borg --version

Если в репозиториях версия старая — ставь через pipx, не мешай системный python с посторонними пакетами:


sudo apt install -y pipx
pipx install borgbackup
pipx ensurepath

Результат: команда borg --version показывает 1.4.x.

Шаг 2. Создание пользователя для бэкапов

Не гоняй Borg под root без причины. Отдельный пользователь снижает ущерб, если что-то пойдёт не так.


sudo useradd -m -s /bin/bash backupuser
sudo mkdir -p /srv/borg-repo
sudo chown backupuser:backupuser /srv/borg-repo

Результат: пользователь backupuser существует, директория репозитория готова и принадлежит ему.

Шаг 3. Инициализация репозитория

Вот тут важно не перепутать режим шифрования. Для локального или сетевого диска бери repokey-blake2 — ключ хранится внутри репозитория, а blake2 быстрее на современных CPU.


sudo -u backupuser borg init --encryption=repokey-blake2 /srv/borg-repo

Borg попросит пароль для ключа. Придумай сложный и сохрани в менеджере паролей — без него бэкап не восстановить, даже имея полный доступ к репозиторию.

Про ключи шифрования
Если используешь repokey — ключ хранится внутри репозитория и уходит вместе с ним при копировании. Если keyfile — ключ лежит только на локальной машине в ~/.config/borg/keys. Потеряешь keyfile и пароль одновременно — данные восстановить не получится никак, даже с полным доступом к серверу хранения.

borg key export /srv/borg-repo /root/borg-key-backup.txt

Результат: файл borg-key-backup.txt с резервной копией ключа. Храни его отдельно от репозитория — на флешке, в сейфе, где угодно, только не рядом с бэкапами.

Шаг 4. Первый бэкап


export BORG_PASSPHRASE='твой-пароль-от-ключа'
sudo -u backupuser -E borg create --stats --progress \
  /srv/borg-repo::backup-{now:%Y-%m-%d_%H-%M} \
  /etc /home /var/www

Результат: архив с именем вида backup-2026-07-28_14-30 внутри репозитория. Флаг —stats покажет объём данных и коэффициент дедупликации сразу после завершения.

Дальше идут архитектурные детали. Ниже — схема того, как Borg работает изнутри, чтобы не гадать, что происходит между командой и репозиторием.

%%{init: {
  'theme': 'base',
  'themeVariables': {
    'primaryColor': '#f8fafc',
    'primaryTextColor': '#1e293b',
    'primaryBorderColor': '#94a3b8',
    'lineColor': '#64748b',
    'fontSize': '15px',
    'fontFamily': 'ui-sans-serif, system-ui, sans-serif'
  },
  'flowchart': {'curve': 'linear', 'nodeSpacing': 50, 'rankSpacing': 50}
}}%%
flowchart TD
    A["Файлы на сервере"] --> B["Разбивка на чанки"]
    B --> C["Хеширование чанков"]
    C --> D{"Чанк уже есть?"}
    D -->|Да| E["Пропустить, ссылка на существующий"]
    D -->|Нет| F["Сжать и зашифровать чанк"]
    F --> G["Записать в репозиторий"]
    E --> H["Архив готов"]
    G --> H
    style A fill:#f8fafc,stroke:#3b82f6,stroke-width:2px,color:#1e40af
    style H fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
    style D fill:#f8fafc,stroke:#f97316,stroke-width:2px,color:#9a3412

Шаг 5. Ротация архивов через borg prune

Без ротации репозиторий будет расти вечно. borg prune удаляет старые архивы по политике хранения — оставляет N последних ежедневных, M еженедельных и так далее.


sudo -u backupuser -E borg prune -v --list \
  --keep-daily=7 --keep-weekly=4 --keep-monthly=6 \
  /srv/borg-repo

Результат: репозиторий содержит не больше 7 дневных, 4 недельных и 6 месячных архивов. Место освобождается физически только после отдельной команды compact.


sudo -u backupuser -E borg compact /srv/borg-repo

Без compact удалённые данные chunks остаются занимать место на диске — Borg просто перестаёт на них ссылаться. Забыл про compact — удивился, почему после prune место не освободилось. Так уже бывало не раз.

Шаг 6. Автоматизация через systemd-timer

Cron работает, но systemd-timer даёт логи через journalctl, зависимости от других юнитов и внятную обработку ошибок. Смотри, вот тут собирается вся автоматизация.


sudo mkdir -p /etc/borg
sudo tee /etc/borg/backup.sh > /dev/null << 'EOF'
#!/bin/bash
set -euo pipefail

export BORG_REPO=/srv/borg-repo
export BORG_PASSPHRASE='твой-пароль-от-ключа'

borg create --stats --compression zstd,6 \
  "$BORG_REPO"::backup-{now:%Y-%m-%d_%H-%M} \
  /etc /home /var/www

borg prune -v --list \
  --keep-daily=7 --keep-weekly=4 --keep-monthly=6 \
  "$BORG_REPO"

borg compact "$BORG_REPO"
EOF
sudo chmod 750 /etc/borg/backup.sh
sudo chown backupuser:backupuser /etc/borg/backup.sh

Файл systemd-сервиса:


[Unit]
Description=BorgBackup daily job
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
User=backupuser
ExecStart=/etc/borg/backup.sh
Nice=19
IOSchedulingClass=idle

Сохрани как /etc/systemd/system/borg-backup.service.

Файл таймера:


[Unit]
Description=Run BorgBackup daily at 03:15

[Timer]
OnCalendar=*-*-* 03:15:00
Persistent=true
RandomizedDelaySec=300

[Install]
WantedBy=timers.target

Сохрани как /etc/systemd/system/borg-backup.timer.


sudo systemctl daemon-reload
sudo systemctl enable --now borg-backup.timer

Результат: systemctl list-timers показывает borg-backup.timer со временем следующего запуска.

4. Проверка: работает ли бэкап на самом деле

Команда завершилась без ошибок — ничего не значит. Проверяй факт.


sudo systemctl status borg-backup.timer
sudo journalctl -u borg-backup.service --since "2 days ago"
sudo -u backupuser borg list /srv/borg-repo

Результат: в списке видны архивы с ожидаемыми датами, journalctl не показывает traceback или ошибок доступа.

Проверка целостности репозитория — отдельная команда, не полагайся только на список архивов:


sudo -u backupuser -E borg check --verify-data /srv/borg-repo

—verify-data гоняет проверку по всем данным, не только по метаданным. Занимает дольше, но именно она ловит битые чанки на диске.

Проверка восстановления — монтирование архива

Смотри, вот тут самое важное. Borg умеет монтировать архив как обычную FUSE-файловую систему — можно зайти и посмотреть содержимое без полной распаковки.


sudo apt install -y borgbackup-fuse
mkdir -p /tmp/borg-mount
sudo -u backupuser -E borg mount /srv/borg-repo::backup-2026-07-28_03-15 /tmp/borg-mount
ls /tmp/borg-mount/etc
sudo -u backupuser borg umount /tmp/borg-mount

Результат: содержимое /etc из архива доступно как обычная папка. Забыл отмонтировать — процесс борга будет висеть в памяти, не пугайся, это нормально до umount.

5. Осложнения

Ошибка Причина Решение Команда
Repository has been created with encryption mode X Пароль в переменной BORG_PASSPHRASE не совпадает с паролем при создании репозитория Проверить, откуда берётся переменная — через export или из другого скрипта borg info /srv/borg-repo
Failed to lock repository Предыдущий процесс borg завис или упал без снятия блокировки Снять stale-lock только если точно уверен, что процесс мёртв borg break-lock /srv/borg-repo
Insufficient free space to complete transaction Диск с репозиторием заполнен, prune и compact не выполнялись давно Запустить prune и compact вручную, проверить свободное место df -h /srv/borg-repo && borg compact /srv/borg-repo
Connection closed by remote host (SSH-репозиторий) Обрыв SSH-сессии на длинном бэкапе, таймаут на стороне сервера Добавить ServerAliveInterval в SSH-конфиг клиента ServerAliveInterval 60
borg-backup.service failed с кодом 1, journalctl пуст Скрипт запускается не от того пользователя, нет прав на репозиторий Проверить владельца директории репозитория и User= в юните stat /srv/borg-repo

Ну и запросы у вас — сказала система и повисла. Именно так выглядит зависший borg check на репозитории в несколько терабайт без —verify-data. Не пугайся, просто жди или запускай в screen/tmux.

6. Альтернативы

  • Restic — похожая модель дедупликации, написан на Go, проще собирается статическим бинарником, но менее гибкая система хранения ключей. Выбираем Borg вместо Restic там, где важна тонкая настройка компрессии и совместимость с borgmatic.
  • Rsnapshot — хардлинки вместо дедупликации по чанкам, проще концептуально, но не умеет шифрование из коробки и хуже работает с бинарными изменениями внутри больших файлов.
  • Duplicity — шифрование через GPG, работает с облачными бэкендами из коробки, но инкрементальные цепочки ломаются при потере одного звена — придётся делать полный бэкап заново.
  • Kopia — более новый инструмент с похожей архитектурой на Borg, имеет веб-интерфейс, но экосистема и документация младше, меньше проверенных сценариев в проде.
  • Borgmatic — не альтернатива, а надстройка над Borg. Даёт YAML-конфиг вместо bash-скрипта и встроенные хуки для баз данных. Если bash-скрипт из шага 6 кажется громоздким — смотри в его сторону.

Основной выбор в пользу Borg — зрелость проекта, дедупликация на уровне чанков и клиентское шифрование без дополнительных обвязок.

7. Профилактика

Мониторинг

Без мониторинга узнаешь о сломанном бэкапе только в момент восстановления. Простейший вариант — healthcheck по HTTP после каждого запуска:


curl -fsS -m 10 --retry 3 https://hc-ping.com/твой-uuid-здесь

Добавь эту строку в конец backup.sh после успешного prune — сервис healthcheck пришлёт алерт, если пинг не пришёл вовремя.

Резервное копирование самого ключа

Что бэкапить Как часто Где хранить
Ключ шифрования (borg key export) При каждой смене пароля Отдельно от репозитория — облако, физический носитель
Файл config репозитория Раз в месяц Вместе с ключом
Сам репозиторий Ежедневно (через шаг 6) Отдельный физический диск или другой сервер

Восстановление ключа при потере:


borg key import /srv/borg-repo /root/borg-key-backup.txt

Обновление Borg

Перед обновлением major-версии обязательно прочитай changelog — между 1.2 и 1.4 менялся формат некоторых внутренних структур.


borg --version
pipx upgrade borgbackup
borg check --verify-data /srv/borg-repo

Откат назад: pipx позволяет установить конкретную версию, если новая ведёт себя странно.


pipx install --force "borgbackup==1.2.9"

Безопасность

  • UFW — разреши SSH только с доверенных IP, если репозиторий на удалённом сервере
  • Fail2ban — на SSH-сервисе, принимающем подключения для бэкапов
  • Отдельный SSH-ключ для backupuser, ограниченный через authorized_keys command=
  • Ограничение по IP на firewall для порта SSH удалённого репозитория
  • Append-only режим borg serve на стороне сервера — защита от компрометации клиента

Append-only включается флагом при запуске удалённого borg serve:


borg serve --restrict-to-path /srv/borg-repo --append-only

С append-only даже получив доступ к клиенту, атакующий не сможет удалить старые архивы — только дописать новые.

8. FAQ

Почему borg create не работает после настройки systemd-timer?

Чаще всего дело в переменной BORG_PASSPHRASE — она не наследуется в systemd-сервисе так же, как в интерактивной оболочке. Пропиши её прямо в скрипте backup.sh или через Environment= в юните, а не через ~/.bashrc.

Как проверить что бэкап Borg работает правильно?

Три уровня проверки: borg list показывает архивы, borg check —verify-data проверяет целостность данных на диске, а реальное восстановление через borg mount или borg extract на тестовую машину — единственный способ убедиться, что данные читаются, а не просто существуют.

Что делать если borg prune не освобождает место на диске?

prune только помечает данные как удалённые в метаданных репозитория. Физическое освобождение места происходит через отдельную команду borg compact — без неё диск не почистится, сколько архивов ни удаляй.

Чем BorgBackup отличается от restic?

Обе утилиты используют похожую модель дедупликации по чанкам. Borg даёт более гибкую настройку компрессии и режимов хранения ключей, restic проще разворачивается как единый статический бинарник и из коробки поддерживает больше облачных бэкендов без дополнительных обвязок вроде rclone.

Можно ли восстановить один файл без распаковки всего архива?

Да, через borg mount архив монтируется как обычная директория, и нужный файл копируется оттуда напрямую. Либо через borg extract с указанием конкретного пути внутри архива — тогда распакуется только он.

9. Прогноз

Настроили репозиторий с клиентским шифрованием, автоматизацию через systemd-timer без cron и ротацию, которая не даёт диску переполниться. Теперь бэкап идёт каждую ночь сам, без напоминаний, а место на диске занято ровно тем, что реально изменилось за сутки.

Дальше дело за дисциплиной: раз в месяц заходи и проверяй восстановление руками, не верь только зелёному статусу systemctl. Если после настройки что-то не заработало — пиши в комментарии, разберёмся.

Архитектура: клиент-сервер через SSH

%%{init: {
  'theme': 'base',
  'themeVariables': {
    'primaryColor': '#f8fafc',
    'primaryTextColor': '#1e293b',
    'primaryBorderColor': '#94a3b8',
    'noteBkgColor': '#fefce8',
    'noteTextColor': '#713f12',
    'noteBorderColor': '#fbbf24',
    'actorBkg': '#f8fafc',
    'actorBorder': '#94a3b8',
    'actorTextColor': '#1e293b',
    'fontSize': '15px',
    'fontFamily': 'ui-sans-serif, system-ui, sans-serif'
  },
  'sequence': {
    'mirrorActors': false,
    'messageAlign': 'center',
    'actorMargin': 120,
    'width': 160,
    'noteMargin': 12
  }
}}%%
sequenceDiagram
    participant T as systemd-timer
    participant C as borg client
    participant S as borg serve
    T->>C: Запуск backup.sh
    C->>S: SSH-соединение
    S->>C: Список известных чанков
    C->>S: Отправка новых чанков
    S->>C: Подтверждение записи
    C->>T: Код завершения 0

Таблица портов

Порт Протокол Назначение Доступен снаружи?
22 TCP/SSH Транспорт для borg serve на удалённом репозитории Только с доверенных IP
Нет отдельного порта Локальный репозиторий работает без сети Нет

Полезные ссылки:


Андрей А.
Author: Андрей А.

Руководитель ИТ / Кризис-менеджер 25 лет в IT: от инженера в МегаФоне до руководителя отдела. Знаю, как выглядит бардак: нестабильные сети, устаревшая инфраструктура, конфликты в команде, раздутые сроки. Помогаю бизнесу выходить из кризиса: навожу порядок в легаси, стабилизирую то, что разваливается, выстраиваю прогнозируемые процессы. Не раз возвращал к жизни ИТ-структуры — знаю цену хаосу. 📍 Ищу проект для полной реорганизации / стабилизации. 📬 Telegram: @over_dude ✉️ mail@it-apteka.com

Оставайтесь на связи

Рецепты от IT-боли. Без воды, без рекламы, без маркетинговой шелухи.

Подписаться на IT-Аптеку →

Мы ВКонтакте

IT-Аптека — советы, новости и помощь рядом.

Вступить в группу ВКонтакте →
Поделитесь:

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Прокрутить вверх