<p>Быстрый ответ. <strong>BorgBackup</strong> — консольная утилита для инкрементальных бэкапов с дедупликацией и шифрованием на клиенте. Ставится через apt/pip, инициализация репозитория — одна команда, автоматизация — через systemd-timer. Первый архив весит как полный бэкап, все последующие — только дельта. Восстановление — через <code>borg extract</code> или монтирование архива как обычной папки.</p>
"Суть
<br />
Установи Borg. Инициализируй репозиторий с шифрованием repokey-blake2. Настрой systemd-timer на ежедневный запуск borg create. Добавь borg prune с политикой хранения 7 daily / 4 weekly / 6 monthly. Раз в месяц проверяй восстановление руками, не доверяй логам вслепую.<br />
<h2>1. Диагноз: почему rsync и tar уже не то</h2>
<p>Поднял продакшн-сервер. Настроил cron с tar раз в ночь. Через полгода диск с бэкапами забит под завязку, потому что каждую ночь пишется полный архив заново. Знакомо? У большинства так и есть — до первого раза, когда бэкап понадобился реально, а места под него не осталось.</p>
<p><strong>BorgBackup</strong> решает это дедупликацией на уровне чанков: система разбивает файлы на переменные блоки, хеширует и хранит только уникальные куски. Инкрементальный бэкап BorgBackup после первого полного архива занимает столько места, сколько реально изменилось — и не байтом больше.</p>
<p>Что получишь на выходе этой статьи:</p>
<ul>
<li>Рабочий зашифрованный репозиторий Borg</li>
<li>Автоматический <a href="https://it-apteka.com/zapusk-bash-skriptov-v-linux-cherez-terminal-cron-python-windows-i-raspberry-pi/" title="Запуск bash скрипта: chmod, cron, Python, Windows и Raspberry Pi" target="_blank" rel="noopener" data-wpil-monitor-id="3154">запуск через systemd-timer без cron</a></li>
<li>Политику ротации, которая не жрёт диск бесконечно</li>
<li>Проверенную процедуру восстановления, а не веру на слово</li>
</ul>
<p>Время на настройку — 30-40 минут, если не тупить в документацию. Нужен доступ по SSH с правами на запись в целевую директорию и python3 актуальной версии.</p>
<h3>Системные требования</h3>
<table>
<tbody>
<tr>
<th>Компонент</th>
<th>Минимальная версия</th>
<th>Рекомендуется</th>
</tr>
<tr>
<td>ОС</td>
<td><a class="wpil_keyword_link" href="https://it-apteka.com/tag/debian/" target="_blank" rel="noopener" title="Debian" data-wpil-keyword-link="linked" data-wpil-monitor-id="3152">Debian</a> 11 / Ubuntu 20.04</td>
<td><a href="https://it-apteka.com/nastrojka-staticheskogo-ip-v-ubuntu-22-04-24-04-i-debian-12-13-nov/" title="Настройка статического IP в Ubuntu 22.04-24.04 и Debian 12-13: Новый мир Netplan" target="_blank" rel="noopener" data-wpil-monitor-id="3155">Debian 12 / Ubuntu</a> 24.04</td>
</tr>
<tr>
<td>Python</td>
<td>3.9</td>
<td>3.10+</td>
</tr>
<tr>
<td>BorgBackup</td>
<td>1.2.x</td>
<td>1.4.x (стабильная ветка)</td>
</tr>
<tr>
<td>OpenSSH (для remote-репозитория)</td>
<td>7.x</td>
<td>9.x</td>
</tr>
</tbody>
</table>
<p>На момент публикации актуальна стабильная версия 1.4.5 (ветка 1.4). Ветка 2.0 пока в бете — не тащи её в продакшн, сколько бы ни хотелось новых плюшек. Перед установкой проверь свежие релизы на <a href="https://www.borgbackup.org/" target="_blank" rel="noopener nofollow">официальном сайте borgbackup.org</a>.</p>
<h2>2. Причины бежать от tar/rsync к Borg</h2>
<table>
<tbody>
<tr>
<th>Причина</th>
<th>Почему это ломает работу без Borg</th>
</tr>
<tr>
<td>Полные копии каждую ночь</td>
<td>Диск с бэкапами растёт линейно, место кончается внезапно и в самый неподходящий момент</td>
</tr>
<tr>
<td>Нет шифрования из коробки</td>
<td>tar + удалённое хранилище = данные лежат открытым текстом на чужом сервере</td>
</tr>
<tr>
<td>Нет проверки целостности</td>
<td>Битый бэкап обнаруживается только в момент восстановления — то есть когда уже поздно</td>
</tr>
<tr>
<td>Ручная ротация через find -mtime</td>
<td>Один неверный флаг — и <a class="wpil_keyword_link" href="https://it-apteka.com/category/scripts/" target="_blank" rel="noopener" title="Скрипты" data-wpil-keyword-link="linked" data-wpil-monitor-id="3147">скрипт</a> удаляет не то, что нужно</td>
</tr>
<tr>
<td>Нет дедупликации между машинами</td>
<td><a href="https://it-apteka.com/bjekap-i-vosstanovlenie-servera-the-dude-mikrotik/" title="Бэкап и восстановление сервера The Dude (MikroTik)" target="_blank" rel="noopener" data-wpil-monitor-id="3156">Бэкапим 10 однотипных серверов</a> — платим за 10x место вместо разумного</td>
</tr>
<tr>
<td>Долгое восстановление отдельного файла</td>
<td>Приходится распаковывать весь архив, чтобы достать один конфиг</td>
</tr>
</tbody>
</table>
<p>Капля никотина убивает лошадь. Одна строка в crontab без комментария — весь отдел, который потом два часа гадает, что за скрипт стирает бэкапы недельной давности.</p>
<h2>3. Рецепт: установка и настройка Borg</h2>
<h3>Подготовка</h3>
<p>Проверь версию Python и наличие пакетного менеджера. Borg тянет за собой не так много зависимостей, но лучше свериться заранее.</p>
<pre><code class="language-bash">
python3 --version
apt list --installed 2>/dev/null | grep -i openssh
</code></pre>
<p>Результат: python3 версии не ниже 3.9, ssh-клиент установлен.</p>
<h3>Шаг 1. Установка BorgBackup</h3>
<p>Два пути — через системный пакет или через pip. Системный пакет проще, pip даёт более свежую версию.</p>
<pre><code class="language-bash">
sudo apt update
sudo apt install -y borgbackup
borg --version
</code></pre>
<p>Если в репозиториях версия старая — ставь через pipx, не мешай системный python с посторонними пакетами:</p>
<pre><code class="language-bash">
sudo apt install -y pipx
pipx install borgbackup
pipx ensurepath
</code></pre>
<p>Результат: команда <code>borg --version</code> показывает 1.4.x.</p>
<h3>Шаг 2. Создание пользователя для бэкапов</h3>
<p>Не гоняй Borg под root без причины. Отдельный пользователь снижает ущерб, если что-то пойдёт не так.</p>
<pre><code class="language-bash">
sudo useradd -m -s /bin/bash backupuser
sudo mkdir -p /srv/borg-repo
sudo chown backupuser:backupuser /srv/borg-repo
</code></pre>
<p>Результат: пользователь backupuser существует, директория репозитория готова и принадлежит ему.</p>
<h3>Шаг 3. Инициализация репозитория</h3>
<p>Вот тут важно не перепутать режим шифрования. Для локального или сетевого диска бери <code>repokey-blake2</code> — ключ хранится внутри репозитория, а blake2 быстрее на современных CPU.</p>
<pre><code class="language-bash">
sudo -u backupuser borg init --encryption=repokey-blake2 /srv/borg-repo
</code></pre>
<p>Borg попросит пароль для ключа. Придумай сложный и сохрани в менеджере паролей — без него бэкап не восстановить, даже имея полный доступ к репозиторию.</p>
"Про
<br />
Если используешь repokey — ключ хранится внутри репозитория и уходит вместе с ним при копировании. Если keyfile — ключ лежит только на локальной машине в ~/.config/borg/keys. Потеряешь keyfile и пароль одновременно — данные восстановить не получится никак, даже с <a href="https://it-apteka.com/linux-sip-proksi-server-polnoe-rukovodstvo-po-kamailio-opensips-i-asterisk/" title="Linux SIP прокси сервер: полное руководство по Kamailio, OpenSIPS и Asterisk" target="_blank" rel="noopener" data-wpil-monitor-id="3153">полным доступом к серверу</a> хранения.<br />
<pre><code class="language-bash">
borg key export /srv/borg-repo /root/borg-key-backup.txt
</code></pre>
<p>Результат: файл borg-key-backup.txt с резервной копией ключа. Храни его отдельно от репозитория — на флешке, в сейфе, где угодно, только не рядом с бэкапами.</p>
<h3>Шаг 4. Первый бэкап</h3>
<pre><code class="language-bash">
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
</code></pre>
<p>Результат: архив с именем вида <a class="wpil_keyword_link" href="https://it-apteka.com/category/rezervnoe-kopirovanie/" target="_blank" rel="noopener" title="Резервное копирование" data-wpil-keyword-link="linked" data-wpil-monitor-id="3148">backup</a>-2026-07-28_14-30 внутри репозитория. Флаг —stats покажет объём данных и коэффициент дедупликации сразу после завершения.</p>
<p>Дальше идут архитектурные детали. Ниже — схема того, как Borg работает изнутри, чтобы не гадать, что происходит между командой и репозиторием.</p>
<pre class="mermaid">%%{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
</pre>
<h3>Шаг 5. Ротация архивов через borg prune</h3>
<p>Без ротации репозиторий будет расти вечно. borg prune удаляет старые архивы по политике хранения — оставляет N последних ежедневных, M еженедельных и так далее.</p>
<pre><code class="language-bash">
sudo -u backupuser -E borg prune -v --list \
--keep-daily=7 --keep-weekly=4 --keep-monthly=6 \
/srv/borg-repo
</code></pre>
<p>Результат: репозиторий содержит не больше 7 дневных, 4 недельных и 6 месячных архивов. Место освобождается физически только после отдельной команды compact.</p>
<pre><code class="language-bash">
sudo -u backupuser -E borg compact /srv/borg-repo
</code></pre>
<p>Без compact удалённые данные chunks остаются занимать место на диске — Borg просто перестаёт на них ссылаться. Забыл про compact — удивился, почему после prune место не освободилось. Так уже бывало не раз.</p>
<h3>Шаг 6. Автоматизация через systemd-timer</h3>
<p>Cron работает, но systemd-timer даёт логи через journalctl, зависимости от других юнитов и внятную обработку ошибок. Смотри, вот тут собирается вся автоматизация.</p>
<pre><code class="language-bash">
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
</code></pre>
<p>Файл systemd-сервиса:</p>
<pre><code class="language-text">
[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
</code></pre>
<p>Сохрани как /etc/systemd/system/borg-backup.service.</p>
<p>Файл таймера:</p>
<pre><code class="language-text">
[Unit]
Description=Run BorgBackup daily at 03:15
[Timer]
OnCalendar=*-*-* 03:15:00
Persistent=true
RandomizedDelaySec=300
[Install]
WantedBy=timers.target
</code></pre>
<p>Сохрани как /etc/systemd/system/borg-backup.timer.</p>
<pre><code class="language-bash">
sudo systemctl daemon-reload
sudo systemctl enable --now borg-backup.timer
</code></pre>
<p>Результат: <code>systemctl list-timers</code> показывает borg-backup.timer со временем следующего запуска.</p>
<h2>4. Проверка: работает ли бэкап на самом деле</h2>
<p>Команда завершилась без ошибок — ничего не значит. Проверяй факт.</p>
<pre><code class="language-bash">
sudo systemctl status borg-backup.timer
sudo journalctl -u borg-backup.service --since "2 days ago"
sudo -u backupuser borg list /srv/borg-repo
</code></pre>
<p>Результат: в списке видны архивы с ожидаемыми датами, journalctl не показывает traceback или ошибок доступа.</p>
<p>Проверка целостности репозитория — отдельная команда, не полагайся только на список архивов:</p>
<pre><code class="language-bash">
sudo -u backupuser -E borg check --verify-data /srv/borg-repo
</code></pre>
<p>—verify-data гоняет проверку по всем данным, не только по метаданным. Занимает дольше, но именно она ловит битые чанки на диске.</p>
<h3>Проверка восстановления — монтирование архива</h3>
<p>Смотри, вот тут самое важное. Borg умеет монтировать архив как обычную FUSE-файловую систему — можно зайти и посмотреть содержимое без полной распаковки.</p>
<pre><code class="language-bash">
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
</code></pre>
<p>Результат: содержимое /etc из архива доступно как обычная папка. Забыл отмонтировать — процесс борга будет висеть в памяти, не пугайся, это нормально до umount.</p>
<h2>5. Осложнения</h2>
<table>
<tbody>
<tr>
<th>Ошибка</th>
<th>Причина</th>
<th>Решение</th>
<th>Команда</th>
</tr>
<tr>
<td>Repository has been created with encryption mode X</td>
<td>Пароль в переменной BORG_PASSPHRASE не совпадает с паролем при создании репозитория</td>
<td>Проверить, откуда берётся переменная — через export или из другого скрипта</td>
<td><code class="language-bash">borg info /srv/borg-repo</code></td>
</tr>
<tr>
<td>Failed to lock repository</td>
<td>Предыдущий процесс borg завис или упал без снятия блокировки</td>
<td>Снять stale-lock только если точно уверен, что процесс мёртв</td>
<td><code class="language-bash">borg break-lock /srv/borg-repo</code></td>
</tr>
<tr>
<td>Insufficient free space to complete transaction</td>
<td>Диск с репозиторием заполнен, prune и compact не выполнялись давно</td>
<td>Запустить prune и compact вручную, проверить свободное место</td>
<td><code class="language-bash">df -h /srv/borg-repo && borg compact /srv/borg-repo</code></td>
</tr>
<tr>
<td>Connection closed by remote host (SSH-репозиторий)</td>
<td>Обрыв SSH-сессии на длинном бэкапе, таймаут на стороне сервера</td>
<td>Добавить ServerAliveInterval в SSH-конфиг клиента</td>
<td><code class="language-text">ServerAliveInterval 60</code></td>
</tr>
<tr>
<td>borg-backup.service failed с кодом 1, journalctl пуст</td>
<td>Скрипт запускается не от того пользователя, нет прав на репозиторий</td>
<td>Проверить владельца директории репозитория и User= в юните</td>
<td><code class="language-bash">stat /srv/borg-repo</code></td>
</tr>
</tbody>
</table>
<p>Ну и запросы у вас — сказала система и повисла. Именно так выглядит зависший borg check на репозитории в несколько терабайт без —verify-data. Не пугайся, просто жди или запускай в screen/tmux.</p>
<h2>6. Альтернативы</h2>
<ul>
<li><strong>Restic</strong> — похожая модель дедупликации, написан на Go, проще собирается статическим бинарником, но менее гибкая система хранения ключей. Выбираем Borg вместо Restic там, где важна тонкая настройка компрессии и совместимость с borgmatic.</li>
<li><strong>Rsnapshot</strong> — хардлинки вместо дедупликации по чанкам, проще концептуально, но не умеет шифрование из коробки и хуже работает с бинарными изменениями внутри больших файлов.</li>
<li><strong>Duplicity</strong> — шифрование через GPG, работает с облачными бэкендами из коробки, но инкрементальные цепочки ломаются при потере одного звена — придётся делать полный бэкап заново.</li>
<li><strong>Kopia</strong> — более новый инструмент с похожей архитектурой на Borg, имеет веб-интерфейс, но экосистема и документация младше, меньше проверенных сценариев в проде.</li>
<li><strong>Borgmatic</strong> — не альтернатива, а надстройка над Borg. Даёт YAML-конфиг вместо <a class="wpil_keyword_link" href="https://it-apteka.com/tag/bash/" target="_blank" rel="noopener" title="Bash" data-wpil-keyword-link="linked" data-wpil-monitor-id="3150">bash</a>-скрипта и встроенные хуки для баз данных. Если bash-скрипт из шага 6 кажется громоздким — смотри в его сторону.</li>
</ul>
<p>Основной выбор в пользу Borg — зрелость проекта, дедупликация на уровне чанков и клиентское шифрование без дополнительных обвязок.</p>
<h2>7. Профилактика</h2>
<h3>Мониторинг</h3>
<p>Без мониторинга узнаешь о сломанном бэкапе только в момент восстановления. Простейший вариант — healthcheck по HTTP после каждого запуска:</p>
<pre><code class="language-bash">
curl -fsS -m 10 --retry 3 https://hc-ping.com/твой-uuid-здесь
</code></pre>
<p>Добавь эту строку в конец backup.sh после успешного prune — сервис healthcheck пришлёт алерт, если пинг не пришёл вовремя.</p>
<h3>Резервное копирование самого ключа</h3>
<table>
<tbody>
<tr>
<th>Что бэкапить</th>
<th>Как часто</th>
<th>Где хранить</th>
</tr>
<tr>
<td>Ключ шифрования (borg key export)</td>
<td>При каждой смене пароля</td>
<td>Отдельно от репозитория — облако, физический носитель</td>
</tr>
<tr>
<td>Файл config репозитория</td>
<td>Раз в месяц</td>
<td>Вместе с ключом</td>
</tr>
<tr>
<td>Сам репозиторий</td>
<td>Ежедневно (через шаг 6)</td>
<td>Отдельный физический диск или другой сервер</td>
</tr>
</tbody>
</table>
<p>Восстановление ключа при потере:</p>
<pre><code class="language-bash">
borg key import /srv/borg-repo /root/borg-key-backup.txt
</code></pre>
<h3>Обновление Borg</h3>
<p>Перед обновлением major-версии обязательно прочитай changelog — между 1.2 и 1.4 менялся формат некоторых внутренних структур.</p>
<pre><code class="language-bash">
borg --version
pipx upgrade borgbackup
borg check --verify-data /srv/borg-repo
</code></pre>
<p>Откат назад: pipx позволяет установить конкретную версию, если новая ведёт себя странно.</p>
<pre><code class="language-bash">
pipx install --force "borgbackup==1.2.9"
</code></pre>
<h3>Безопасность</h3>
<ul>
<li>UFW — разреши SSH только с доверенных IP, если репозиторий на удалённом сервере</li>
<li>Fail2ban — на SSH-сервисе, принимающем подключения для бэкапов</li>
<li>Отдельный SSH-ключ для backupuser, ограниченный через authorized_keys command=</li>
<li>Ограничение по IP на firewall для порта SSH удалённого репозитория</li>
<li>Append-only режим borg serve на стороне сервера — <a class="wpil_keyword_link" href="https://it-apteka.com/category/security/" target="_blank" rel="noopener" title="Безопасность" data-wpil-keyword-link="linked" data-wpil-monitor-id="3149">защита</a> от компрометации клиента</li>
</ul>
<p>Append-only включается флагом при запуске удалённого borg serve:</p>
<pre><code class="language-bash">
borg serve --restrict-to-path /srv/borg-repo --append-only
</code></pre>
<p>С append-only даже получив доступ к клиенту, атакующий не сможет удалить старые архивы — только дописать новые.</p>
<h2>8. FAQ</h2>
<h3>Почему borg create не работает после настройки systemd-timer?</h3>
<p>Чаще всего дело в переменной BORG_PASSPHRASE — она не наследуется в systemd-сервисе так же, как в интерактивной оболочке. Пропиши её прямо в скрипте backup.sh или через Environment= в юните, а не через ~/.bashrc.</p>
<h3>Как проверить что бэкап Borg работает правильно?</h3>
<p>Три уровня проверки: borg list показывает архивы, borg check —verify-data проверяет целостность данных на диске, а реальное восстановление через borg mount или borg extract на тестовую машину — единственный способ убедиться, что данные читаются, а не просто существуют.</p>
<h3>Что делать если borg prune не освобождает место на диске?</h3>
<p><a href="https://it-apteka.com/docker-volumes-kak-hranit-dannye-kontejnerov-i-ne-poterjat-ih-posle-prune/" title="Docker volumes: как хранить данные контейнеров и не потерять их после prune" target="_blank" rel="noopener" data-wpil-monitor-id="3157">prune только помечает данные</a> как удалённые в метаданных репозитория. Физическое освобождение места происходит через отдельную команду borg compact — без неё диск не почистится, сколько архивов ни удаляй.</p>
<h3>Чем BorgBackup отличается от restic?</h3>
<p>Обе утилиты используют похожую модель дедупликации по чанкам. Borg даёт более гибкую настройку компрессии и режимов хранения ключей, restic проще разворачивается как единый статический бинарник и из коробки поддерживает больше облачных бэкендов без дополнительных обвязок вроде rclone.</p>
<h3>Можно ли восстановить один файл без распаковки всего архива?</h3>
<p>Да, через borg mount архив монтируется как обычная директория, и нужный файл копируется оттуда напрямую. Либо через borg extract с указанием конкретного пути внутри архива — тогда распакуется только он.</p>
<h2>9. Прогноз</h2>
<p>Настроили репозиторий с клиентским шифрованием, автоматизацию через systemd-timer без cron и ротацию, которая не даёт диску переполниться. Теперь бэкап идёт каждую ночь сам, без напоминаний, а место на диске занято ровно тем, что реально изменилось за сутки.</p>
<p>Дальше дело за дисциплиной: раз в месяц заходи и проверяй восстановление руками, не верь только зелёному статусу systemctl. Если после настройки что-то не заработало — пиши в комментарии, разберёмся.</p>
<h2>Архитектура: клиент-сервер через SSH</h2>
<pre class="mermaid">%%{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
</pre>
<h2>Таблица портов</h2>
<table>
<tbody>
<tr>
<th>Порт</th>
<th>Протокол</th>
<th>Назначение</th>
<th>Доступен снаружи?</th>
</tr>
<tr>
<td>22</td>
<td>TCP/SSH</td>
<td>Транспорт для borg serve на удалённом репозитории</td>
<td>Только с доверенных IP</td>
</tr>
<tr>
<td>Нет отдельного порта</td>
<td>—</td>
<td>Локальный репозиторий работает без <a class="wpil_keyword_link" href="https://it-apteka.com/category/networks/" target="_blank" rel="noopener" title="Сети" data-wpil-keyword-link="linked" data-wpil-monitor-id="3151">сети</a></td>
<td>Нет</td>
</tr>
</tbody>
</table>
<hr />
<p><strong>Полезные ссылки:</strong></p>
<ul>
<li><a href="https://www.borgbackup.org/" target="_blank" rel="noopener nofollow">Официальный сайт BorgBackup</a></li>
<li><a href="https://borgbackup.readthedocs.io/" target="_blank" rel="noopener nofollow">Документация BorgBackup</a></li>
<li><a href="https://github.com/borgbackup/borg" target="_blank" rel="nofollow noopener">Репозиторий на GitHub</a></li>
<li><a href="https://torsion.org/borgmatic/" target="_blank" rel="noopener nofollow">Borgmatic — обёртка с YAML-конфигом</a></li>
</ul>
<p><!-- ============================================================ Schema.org разметка (вставить в head страницы или через плагин Schema) ============================================================ --><br />
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "HowTo",
"name": "BorgBackup: инкрементальный бэкап с шифрованием и автоматизацией через systemd",
"description": "Пошаговая настройка BorgBackup: инициализация репозитория, шифрование, borg create, borg prune и автоматизация через systemd-timer",
"step": [
{"@type": "HowToStep", "name": "Установка BorgBackup", "text": "Установить пакет borgbackup через apt или pipx"},
{"@type": "HowToStep", "name": "Инициализация репозитория", "text": "Создать репозиторий с шифрованием repokey-blake2"},
{"@type": "HowToStep", "name": "Первый бэкап", "text": "Запустить borg create для первого полного архива"},
{"@type": "HowToStep", "name": "Ротация архивов", "text": "Настроить borg prune с политикой хранения"},
{"@type": "HowToStep", "name": "Автоматизация", "text": "Создать systemd-service и systemd-timer для ежедневного запуска"}
]
}
</script></p>
Быстрый ответ. 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 |
| Нет отдельного порта |
— |
Локальный репозиторий работает без сети |
Нет |
Полезные ссылки: