BorgBackup это инструмент, который я рекомендую каждому системному администратору, управляющему production-серверами. В отличие от простых tar-архивов или rsync, Borg предоставляет дедупликацию на уровне блоков, шифрование и сжатие «из коробки». Если вы измените 1 байт в файле размером 10 ГБ, Borg сохранит только этот изменённый блок, а не весь файл заново.
В этой статье покажу полный production-ready workflow от установки до автоматизации через cron с резервным копированием баз данных и отправкой бэкапов на удалённый сервер через SSH.
Установка BorgBackup
На большинстве современных дистрибутивов Borg доступен в репозиториях, поэтому установка проста:
# Debian/Ubuntu
sudo apt update && sudo apt install borgbackup
# CentOS/RHEL 8+
sudo dnf install borgbackup
# Arch Linux
sudo pacman -S borg
На момент написания статьи актуальная версия была 1.2 Проверить можно командой:
borg --version
Установка и инициализация локального репозитория
Начнем с локального хранения бэкапов. Понятно, что лучше так не делать. И в случае проблем с сервером, локальные бэкапы также окажутся недоступны. И вроде как смысла в этом нет. Да, действительно на продакт-сервере однозначно должно быть реализовано удаленное хранение резервных копий! Но в каких-то случаях локальные бэкапы в дополнение к удаленным могут быть полезны. Например, для целей оперативного восстановления.
И так, создаем локальный репозиторий:
mkdir -p /var/backups/borg-repo
Инициализируйте репозиторий с шифрованием repokey-blake2 (ключ хранится в самом репозитории, но требует пароль):
borg init --encryption=repokey-blake2 /var/backups/borg-repo
Borg попросит ввести пароль. Сохраните его в надёжном месте, т.к. без пароля восстановить данные будет невозможно!
Альтернативные режимы шифрования:
repokey— ключ в репозитории (для удалённых бэкапов).keyfile— ключ хранится локально в~/.config/borg/keys/.none— без шифрования.
Понятно, что бэкап должен включать не только файлы сайта, но и актуальные дампы баз данных. Не используйте root-пользователя для создания дампа. Создайте специального пользователя с правами только на чтение (более подробно тут писал):
CREATE USER 'backup_user'@'localhost' IDENTIFIED BY 'strong_password';
GRANT SELECT, LOCK TABLES, SHOW VIEW, EVENT, TRIGGER ON *.* TO 'backup_user'@'localhost';
FLUSH PRIVILEGES;
Если не хотите чтобы сохранялись все базы данных, то вместо …ON *.* TO 'backup_user'@'localhost' прописывайте доступ пользователя к конкретной базе данных. Например, …ON database_name.* TO 'backup_user'@'localhost'.
Сохраните credentials в файл /root/.my.cnf (права 600):
[client]
user=backup_user
password=strong_password
Установите права:
chmod 600 /root/.my.cnf
Скрипт для создания дампа баз данных перед бэкапом
Создайте директорию для временных дампов:
mkdir -p /var/backups/mysql
Создайте скрипт /usr/local/bin/dump_mysql.sh (я использую примерно такой):
#!/bin/bash
# Дамп всех баз MySQL перед бэкапом
DUMP_DIR="/var/backups/mysql"
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
# Создаём дамп всех баз
mysqldump --defaults-file=/root/.my.cnf \
--all-databases \
--single-transaction \
--quick \
--lock-tables=false \
| gzip > $DUMP_DIR/all-databases-$TIMESTAMP.sql.gz
# Удаляем старые дампы (старше 2 дней)
find $DUMP_DIR -type f -name "*.sql.gz" -mtime +2 -delete
echo "MySQL dump created: $DUMP_DIR/all-databases-$TIMESTAMP.sql.gz"
Сделайте скрипт исполняемым:
chmod +x /usr/local/bin/dump_mysql.sh
Создание бэкапа с Borg
Теперь создадим архив, включающий файлы сайта и дамп MySQL. Для этого создадим скрипт, который можно будет потом запускать по cron-у. Название, например /root/borg_local_backup.sh
#!/bin/bash
# Сначала создаём дамп
/usr/local/bin/dump_mysql.sh
# Затем запускаем Borg
export BORG_PASSPHRASE='пароль_репозитория'
borg create \
--stats \
--compression zstd,5 \
--exclude '/var/www/*/cache' \
--exclude '*.log' \
/var/backups/borg-repo::'{hostname}-{now:%Y-%m-%d_%H:%M:%S}' \
/var/www \
/etc/nginx \
/etc/php \
/var/backups/mysql
Разбор параметров:
--stats— показывает статистику после создания архива.--compression zstd,5— сжатие Zstandard (уровень 5, баланс скорости и размера).--exclude— исключает ненужные файлы (кэш, логи).'{hostname}-{now}'— шаблон имени архива (например,web1-2025-12-03_19:30:00).
Какие есть узкие места в этом нашем сценарии.
Первое, это конечно сам скрипт borg_local_backup.sh, он содержит пароль от бекапа. Доступ к нему должен быть ограничен, например:
chmod 600 /root/borg_local_backup.sh
Тут и везде выше root привожу в качестве вашего пользователя, но можно (и даже лучше) использовать и того, под которым заходите. Если, конечно, у него есть доступ к файлам для бэкапов.
Второе, это дамп баз данных. Он никак не защищен, а в БД как правило хранятся какие-то чувствительные данные. Поэтому надо ограничить доступ к директории с дампами и сразу после создания архива удалять дампы. Дописать в конец скрипта /root/borg_local_backup.sh команду:
rm -r /var/backups/mysql/*
Удалённое хранение, настройка SSH и репозитория
Как уже сказал выше, локальный бэкап на том же сервере не защитит вас от отказа диска или пожара в дата-центре. Отправим копии на удалённый сервер.
Генерация SSH-ключа для Borg
На сервере, с которого делаем бэкап (client) генерируем пару ключей:
ssh-keygen -t ed25519 -f ~/.ssh/borg_backup -C "borg-backup-key"
Оставьте passphrase пустым (ключ будет использоваться cron-джобом). Затем, скопируйте публичный ключ на удалённый сервер:
ssh-copy-id -i ~/.ssh/borg_backup.pub user@backup-server.com
Тут предполагаем, что доступ к удалённому серверу (backup-server.com) для пользователя (user) уже есть. Если используете какие-то облачные сервисы, то загрузите ключ через их панель управления или через SFTP в файл .ssh/authorized_keys.
Инициализация удалённого репозитория
Для начала, экспортируйте переменную окружения с указанием SSH-ключа:
export BORG_RSH='ssh -i ~/.ssh/borg_backup'
Затем, инициализируйте репозиторий на удалённом сервере:
borg init --encryption=repokey-blake2 user@backup-server.com:/path/to/borg-repo
Borg автоматически создаст директорию, если её нет (при наличии у вашего пользователя прав).
Создание бэкапа на удалённом сервере
Теперь командой borg create, заменив, разумеется, путь к репозиторию создадим бекап на удаленном сервере:
borg create \
--stats \
--compression zstd,5 \
user@backup-server.com:/path/to/borg-repo::'{hostname}-{now:%Y-%m-%d_%H:%M:%S}' \
/var/www \
/etc/nginx \
/var/backups/mysql
Borg передаст данные через SSH, используя дедупликацию до отправки. Но, понятное дело, эта команда может быть использована один раз с целью тестировки всех настроек. Руками бэкапы никто не делает. Поэтому переходим к следующему разделу.
Автоматизация через Cron с защитой от «залипших» процессов
Создадим полный production-скрипт для создания удаленных бэкапов. Создайте файл /usr/local/bin/borg_backup.sh:
#!/bin/bash
set -e
# === Конфигурация ===
export BORG_REPO='user@backup-server.com:/path/to/borg-repo'
export BORG_PASSPHRASE='ваш_пароль'
export BORG_RSH='ssh -i /root/.ssh/borg_backup'
LOG_FILE="/var/log/borg_backup.log"
LOCK_FILE="/var/run/borg_backup.lock"
# Функция логирования
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
# Проверка залипшего процесса
if [ -f "$LOCK_FILE" ]; then
log "ERROR: Предыдущий бэкап ещё выполняется. Пропускаем."
exit 1
fi
# Создаём lock-файл
touch "$LOCK_FILE"
trap "rm -f $LOCK_FILE" EXIT
# === Дамп MySQL ===
log "Starting MySQL dump..."
/usr/local/bin/dump_mysql.sh
# === Создание архива ===
log "Starting Borg backup..."
borg create \
--stats \
--compression zstd,5 \
--exclude '/var/www/*/cache' \
--exclude '*.log' \
$BORG_REPO::'{hostname}-{now:%Y-%m-%d_%H:%M:%S}' \
/var/www \
/etc/nginx \
/etc/php \
/var/backups/mysql
# === Очистка дампа MySQL ===
rm -r /var/backups/mysql/*
# === Прунинг (удаление старых архивов) ===
log "Pruning old backups..."
borg prune \
--keep-daily=7 \
--keep-weekly=4 \
--keep-monthly=6 \
$BORG_REPO
log "Backup completed successfully!"
Сделайте скрипт исполняемым:
chmod 700 /usr/local/bin/borg_backup.sh
--keep-daily=7— храним дневные бэкапы за последние 7 дней.--keep-weekly=4— храним еженедельные за последний месяц.--keep-monthly=6— храним месячные за полгода.
Добавление в Cron
Будем запускать бэкап ежедневно в 3 ночи:
crontab -e
и добавим строку:
0 3 * * * /usr/local/bin/borg_backup.sh >> /var/log/borg_backup.log 2>&1
На следующий день проверим, все должно работать. Если нет, то смотрим логи, ищем что нет как. По поводу логов, их крайне желательно поставить в logrotate иначе могут сильно раздуться. Да и дисковое пространство тоже надо экономить.
Мониторинг
Проблема в том, что cron выполняется молча. Если скрипт упал, вы узнаёте об этом только при попытке восстановления. Как узнать, что бэкап сломался?
Решение 1. Использовать сторонние сервисы. Например, Healthchecks.io
Зарегистрируйтесь на Healthchecks.io (бесплатно до 20 проверок). Затем, создайте новый check и скопируйте URL (например, https://hc-ping.com/ваш-уникальный-uuid). И добавьте в конец скрипта borg_backup.sh такую строку (до или после последнего лога):
# В случае успеха отправляем "пинг"
curl -fsS -m 10 --retry 5 https://hc-ping.com/ваш-uuid > /dev/null
Если бэкап не выполнится (или скрипт упадёт с ошибкой), Healthchecks не получит «пинг» и отправит вам уведомление в Telegram/Email в зависимости от того, как там настроите.
Решение 2. Проверка последнего бэкапа через скрипт
Если посещаете сервер почти каждый день или достаточно часто, то такое решение вполне вам подойдёт. Создайте /usr/local/bin/check_last_backup.sh:
#!/bin/bash
export BORG_REPO='user@backup-server.com:/path/to/borg-repo'
export BORG_PASSPHRASE='ваш_пароль'
export BORG_RSH='ssh -i /root/.ssh/borg_backup'
LAST_BACKUP=$(borg list --last 1 $BORG_REPO | awk '{print $1}')
LAST_DATE=$(echo $LAST_BACKUP | cut -d'-' -f2,3 | cut -d'_' -f1)
CURRENT_DATE=$(date +%Y-%m-%d)
if [ "$LAST_DATE" != "$CURRENT_DATE" ]; then
echo "WARNING: Last backup is from $LAST_DATE (expected $CURRENT_DATE)"
exit 1
fi
echo "OK: Backup is fresh ($LAST_DATE)"
Добавьте в /root/.bashrc (или для пользователя от которого ходите на сервер) для уведомления при логине:
/usr/local/bin/check_last_backup.sh
Либо можете воспользоваться системой информирования при входе motd, тогда в директорию /etc/update-motd.d/ поместите исполняемый файл и в него поместите ссылку выше. И теперь каждый раз при входе на сервер будете видеть информацию о бекапах.
Восстановление данных из бэкапа
Просмотр списка архивов:
borg list user@backup-server.com:/path/to/borg-repo
Просмотр содержимого конкретного архива:
borg list user@backup-server.com:/path/to/borg-repo::web1-2025-12-01_03:00:00
Восстановление всего архива:
cd /restore
borg extract user@backup-server.com:/path/to/borg-repo::web1-2025-12-01_03:00:00
Восстановление только одного файла:
borg extract user@backup-server.com:/path/to/borg-repo::web1-2025-12-01_03:00:00 var/www/site.com/wp-config.php
Заключение. Чек-лист для production
- Установлен BorgBackup (версия 1.2+).
- Создан SSH-ключ без passphrase для автоматизации.
- Инициализирован удалённый репозиторий с шифрованием.
- Скрипт дампа MySQL выполняется перед бэкапом.
- Cron-задача настроена с логированием.
- Прунинг очищает старые архивы автоматически.
- Мониторинг через Healthchecks.io или login-скрипт.
- Пароль от репозитория сохранён куда-то? Где? Как называется?.
- Проведена тестовая проверка восстановления (хотя бы раз в квартал).
Бэкап, который не проверен восстановлением, не существует. Потратьте час на настройку сейчас и сэкономите дни (или недели) работы при аварии.