Новинка Виртуальный VDS сервер в Нидерландах от 490р
Выберите продукт

BorgBackup на Debian/Ubuntu: бэкап VDS по SSH через systemd timer

Покажу, как на Debian и Ubuntu настроить BorgBackup для VDS: отдельный SSH-ключ, репозиторий на удалённом сервере, скрипт с borg create и borg prune, systemd timer, проверка бэкапа и быстрое восстановление.
BorgBackup на Debian/Ubuntu: бэкап VDS по SSH через systemd timer

Привет, это Вася. В этой инструкции соберём рабочую схему резервного копирования для Debian/Ubuntu на VDS: BorgBackup с удалённым репозиторием по SSH и запуском через systemd timer. Цель не просто «сделать бэкап», а получить предсказуемый процесс: архивы создаются автоматически, старые версии чистятся через borg prune, ошибки видны в журнале, а восстановление проверяется заранее.

BorgBackup удобен для серверных бэкапов тем, что умеет дедупликацию, сжатие и шифрование на стороне клиента. Если на VDS много похожих файлов, ежедневные копии не будут занимать место как полные архивы: Borg сохраняет новые блоки данных, а не весь каталог заново.

Главное правило бэкапов простое: бэкап существует только после успешного тестового восстановления. Пока вы не пробовали извлечь файлы из архива, это не бэкап, а надежда.

Схема: что и куда будем копировать

В примере есть два сервера. Первый — рабочий VDS с Debian или Ubuntu, где живут сайт, база данных и конфиги. Второй — отдельный сервер или хранилище с доступом по SSH, куда складывается репозиторий Borg. Это может быть отдельный VDS для резервных копий, выделенный backup-хост или другая площадка. Важно, чтобы хранилище было физически и логически отдельно от основного сервера.

Бэкап будем запускать от root на рабочем сервере, потому что обычно нужны права на /etc, /var/www, дампы баз и служебные каталоги. На удалённой стороне создадим отдельного пользователя backup. У него будет только каталог для репозитория и SSH-доступ по ключу.

Минимальный набор, который разумно сохранять на типичном веб-сервере:

  • /etc — конфигурация системы и сервисов;
  • /var/www или другой каталог проектов;
  • дампы MySQL/MariaDB/PostgreSQL, если база работает на этом же VDS;
  • важные пользовательские файлы: uploads, media, private storage;
  • список установленных пакетов и версия ОС, чтобы быстрее собрать окружение заново.

Не стоит бездумно копировать всё подряд. Каталоги /proc, /sys, /dev, /run, временные файлы и кэши в бэкапе обычно не нужны. Для Docker-проектов отдельно проверьте, где лежат volumes и как корректно сделать дампы баз. Копировать живые файлы базы данных без согласованной остановки, snapshot-механизма или логического дампа — частая причина бесполезных архивов.

Установка BorgBackup на Debian и Ubuntu

В репозиториях Debian и Ubuntu BorgBackup обычно доступен как пакет borgbackup. Для большинства задач этого достаточно: пакет обновляется через стандартный механизм ОС, не требует ручной установки бинарников и нормально работает с systemd.

sudo apt update
sudo apt install borgbackup openssh-client
borg --version

На удалённом backup-сервере Borg тоже должен быть установлен, потому что при доступе по SSH локальный клиент запускает на той стороне borg serve. Если удалённая сторона тоже Debian/Ubuntu, установка такая же:

sudo apt update
sudo apt install borgbackup openssh-server

Проверьте версии. Небольшая разница обычно допустима, но лучше держать Borg на обоих концах в пределах одной стабильной ветки дистрибутива. Если один сервер на старом Debian, а другой на новой Ubuntu, сначала протестируйте создание архива и восстановление на небольшом каталоге.

Схема резервного копирования VDS в удалённый репозиторий BorgBackup

Подготовка пользователя и каталога на backup-сервере

На сервере, где будет храниться репозиторий, создадим отдельного пользователя без лишних привилегий. Домашний каталог пригодится для authorized_keys, а репозитории можно держать в /srv/borg.

sudo adduser --disabled-password --gecos '' backup
sudo install -d -o backup -g backup -m 700 /srv/borg/web01

Имя web01 условное. Удобно называть репозитории по имени сервера или роли: web01, db01, crm-prod. Не складывайте несколько несвязанных серверов в один репозиторий без необходимости: проще управлять правами, retention-политикой и восстановлением, когда у каждого VDS свой Borg-репозиторий.

На рабочем сервере создадим отдельный SSH-ключ для бэкапов. Не используйте личный админский ключ: если он попадёт в скрипты, логи или чужой доступ, область риска станет шире.

sudo install -d -m 700 /root/.ssh
sudo ssh-keygen -t ed25519 -f /root/.ssh/borg_backup_ed25519 -N '' -C 'borg-backup-web01'

Скопируйте публичную часть ключа на backup-сервер в файл /home/backup/.ssh/authorized_keys. Если парольный доступ разрешён временно, можно использовать ssh-copy-id. Если нет — добавьте ключ через консоль провайдера или другой административный канал.

sudo cat /root/.ssh/borg_backup_ed25519.pub

На backup-сервере права должны быть строгими:

sudo install -d -o backup -g backup -m 700 /home/backup/.ssh
sudo nano /home/backup/.ssh/authorized_keys
sudo chown backup:backup /home/backup/.ssh/authorized_keys
sudo chmod 600 /home/backup/.ssh/authorized_keys

Проверьте SSH-соединение с рабочего VDS:

sudo ssh -i /root/.ssh/borg_backup_ed25519 backup@backup-host borg --version

Если команда возвращает версию Borg, связка SSH и удалённый бинарник работают. На этом этапе полезно зафиксировать fingerprint хоста в /root/.ssh/known_hosts, чтобы автоматический бэкап не зависал на вопросе подтверждения ключа.

Инициализация зашифрованного репозитория

Borg поддерживает разные режимы шифрования. Для серверного бэкапа обычно выбирают repokey-blake2 или современный эквивалент, доступный в вашей версии Borg. Ключ хранится в репозитории, но защищён паролем. Если backup-сервер будет скомпрометирован, содержимое архивов без passphrase не прочитают.

Создадим файл с переменными окружения. Он будет доступен только root. В нём укажем репозиторий, SSH-команду с отдельным ключом и passphrase.

sudo install -d -m 700 /etc/borg
sudo nano /etc/borg/web01.env
sudo chmod 600 /etc/borg/web01.env

Пример содержимого /etc/borg/web01.env:

BORG_REPO='ssh://backup@backup-host/srv/borg/web01'
BORG_RSH='ssh -i /root/.ssh/borg_backup_ed25519 -o IdentitiesOnly=yes'
BORG_PASSPHRASE='replace-with-long-random-passphrase'
BORG_COMPRESSION='zstd,6'

Для passphrase используйте длинную случайную строку и сохраните её отдельно от рабочего сервера: в менеджере секретов, офлайн-хранилище или защищённой документации аварийного восстановления. Если потерять пароль и рабочий сервер, зашифрованный репозиторий останется набором нечитаемых данных.

Теперь инициализируем репозиторий. Команды ниже выполняются в root-shell, чтобы файл окружения прочитался без раскрытия секрета обычному пользователю.

sudo -i
set -a
. /etc/borg/web01.env
set +a
borg init --encryption=repokey-blake2

После инициализации сразу проверьте, что репозиторий отвечает:

borg info
Виртуальный хостинг FastFox
Виртуальный хостинг для сайтов
Универсальное решение для создания и размещения сайтов любой сложности в Интернете от 95₽ / мес

Скрипт резервного копирования

Скрипт нужен не потому, что systemd не умеет запускать Borg напрямую. Просто в скрипте проще подготовить дампы баз, задать исключения, выполнить borg create, затем borg prune и проверку. Также скрипт удобно запускать вручную при отладке.

Создадим каталог для дампов и сам скрипт:

sudo install -d -m 700 /root/backup-work
sudo nano /usr/local/sbin/borg-web01-backup
sudo chmod 700 /usr/local/sbin/borg-web01-backup

Пример скрипта. Он делает список пакетов, при наличии mysqldump сохраняет дамп всех баз, создаёт архив и применяет retention-политику. Подстройте пути под свой сервер.

#!/usr/bin/env bash
set -Eeuo pipefail

export BORG_REPO
export BORG_RSH
export BORG_PASSPHRASE
export BORG_COMPRESSION

WORKDIR=/root/backup-work
HOSTNAME_SHORT=$(hostname -s)
DATE_STAMP=$(date +%Y-%m-%d_%H-%M-%S)
ARCHIVE_NAME=${HOSTNAME_SHORT}-${DATE_STAMP}

cleanup() {
  rm -f "${WORKDIR}/mysql-all.sql"
}
trap cleanup EXIT

mkdir -p "${WORKDIR}"
dpkg-query -W -f='${binary:Package} ${Version}\n' > "${WORKDIR}/packages.txt"

if command -v mysqldump > /dev/null 2>&1; then
  mysqldump --all-databases --single-transaction --routines --triggers --events > "${WORKDIR}/mysql-all.sql"
fi

borg create --verbose --filter AME --list --stats --show-rc --compression "${BORG_COMPRESSION:-zstd,6}" --exclude-caches --exclude /var/cache --exclude /var/tmp --exclude /tmp --exclude /var/lib/mysql --exclude /var/lib/postgresql ::"${ARCHIVE_NAME}" /etc /root/backup-work /var/www /home

borg prune --verbose --list --show-rc --keep-daily=7 --keep-weekly=4 --keep-monthly=6

if borg help compact > /dev/null 2>&1; then
  borg compact --verbose
fi

Обратите внимание на исключения. В примере исключены каталоги с живыми файлами MySQL и PostgreSQL, потому что для баз мы используем логические дампы. Если у вас PostgreSQL, замените блок с mysqldump на pg_dumpall или отдельные pg_dump для нужных баз. Для сложных базовых сценариев пригодятся отдельные материалы про PITR для PostgreSQL через WAL и восстановление MySQL/MariaDB через binlog и GTID.

Команда borg prune в примере хранит 7 ежедневных, 4 недельных и 6 месячных копий. Это нормальная отправная точка для небольшого VDS, но не универсальная политика. Если ошибку в данных замечают через два месяца, хранить только неделю архивов опасно.

Systemd service: запуск бэкапа как управляемой задачи

Теперь оформим скрипт как systemd-сервис. Тип oneshot подходит для задач, которые запускаются, выполняют работу и завершаются. Файл окружения подключим через EnvironmentFile, чтобы passphrase не лежал в unit-файле.

sudo nano /etc/systemd/system/borg-web01-backup.service
[Unit]
Description=BorgBackup for web01
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
EnvironmentFile=/etc/borg/web01.env
ExecStart=/usr/local/sbin/borg-web01-backup
Nice=10
IOSchedulingClass=best-effort
IOSchedulingPriority=7
PrivateTmp=yes
ProtectSystem=full
ReadWritePaths=/root/backup-work
ReadWritePaths=/root/.cache/borg
ReadWritePaths=/root/.config/borg

[Install]
WantedBy=multi-user.target

Параметры Nice, IOSchedulingClass и IOSchedulingPriority помогают не мешать основным сервисам. Бэкап может активно читать диск и сжимать данные, поэтому на маленьком VDS лучше запускать его ночью или в период низкой нагрузки. ProtectSystem=full немного ограничивает доступ на запись, а ReadWritePaths явно разрешает рабочие каталоги Borg и временные дампы.

Перечитаем unit-файлы и запустим бэкап вручную:

sudo systemctl daemon-reload
sudo systemctl start borg-web01-backup.service
sudo systemctl status borg-web01-backup.service

Логи смотрим через journalctl:

sudo journalctl -u borg-web01-backup.service -n 100 --no-pager

На первом запуске архив может создаваться долго: Borg должен прочитать все данные, разбить их на chunks, сжать и отправить на удалённый сервер. Следующие запуски обычно заметно быстрее, если изменилось мало файлов.

Systemd timer вместо cron

Для регулярного запуска используем systemd timer. По сравнению с cron он лучше интегрирован с журналом, умеет случайную задержку через RandomizedDelaySec, может догонять пропущенные запуски через Persistent и удобно проверяется через systemctl list-timers.

sudo nano /etc/systemd/system/borg-web01-backup.timer
[Unit]
Description=Run BorgBackup for web01 daily

[Timer]  03:20:00
RandomizedDelaySec=20m
Persistent=true
Unit=borg-web01-backup.service

[Install]
WantedBy=timers.target

Здесь бэкап запланирован примерно на 03:20, но systemd может сдвинуть запуск на случайное время в пределах 20 минут. Это полезно, если несколько VDS бэкапятся на один backup-сервер: они не начнут одновременно давить сеть и диск. Параметр Persistent=true означает, что если сервер был выключен в момент планового запуска, задача выполнится после загрузки.

sudo systemctl daemon-reload
sudo systemctl enable --now borg-web01-backup.timer
systemctl list-timers borg-web01-backup.timer

Если нужно временно остановить расписание, отключайте timer, а не service:

sudo systemctl disable --now borg-web01-backup.timer

Проверка расписания systemd timer и восстановление архива BorgBackup

Проверка архивов: list, info, check

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

sudo systemctl start borg-web01-backup.service
sudo journalctl -u borg-web01-backup.service -n 200 --no-pager

Для просмотра архивов можно временно загрузить окружение в shell root. Не оставляйте терминал без присмотра, если в нём доступны секреты.

sudo -i
set -a
. /etc/borg/web01.env
set +a
borg list
borg info ::$(borg list --short | tail -n 1)

Команда borg check проверяет целостность репозитория. Полная проверка может быть тяжёлой, поэтому её не всегда стоит запускать после каждого ежедневного бэкапа. Практичный вариант — ежедневное создание и prune, а borg check раз в неделю или месяц отдельным timer.

borg check --repository-only
borg check --archives-only --last 3

Если хранилище медленное, начинайте с --repository-only и проверки последних архивов. Важно найти баланс: проверка должна быть регулярной, но не должна превращаться в ночную атаку на диск и сеть.

Восстановление: borg restore без паники

В Borg нет отдельной команды borg restore в стиле некоторых backup-систем: восстановление обычно делают через borg extract или монтирование архива. Но администраторы часто ищут именно «borg restore», поэтому зафиксируем практический сценарий восстановления.

Сначала посмотрите список архивов:

borg list

Посмотрите содержимое конкретного архива:

borg list ::web01-2026-01-15_03-25-10

Чтобы восстановить один файл в текущий каталог, создайте временную директорию и извлеките нужный путь. Borg сохраняет пути относительно корня без начального слеша, поэтому для /etc/nginx/nginx.conf путь будет etc/nginx/nginx.conf.

mkdir -p /root/restore-test
cd /root/restore-test
borg extract ::web01-2026-01-15_03-25-10 etc/nginx/nginx.conf

Для восстановления сайта в отдельную директорию:

mkdir -p /root/restore-www
cd /root/restore-www
borg extract ::web01-2026-01-15_03-25-10 var/www

Не извлекайте архив сразу в / на живом сервере, если не уверены в результате. Безопаснее восстановить во временный каталог, сравнить файлы, владельцев и права, а затем перенести нужное. Для баз данных сначала извлеките дамп, проверьте его размер и заголовок, затем импортируйте в тестовую базу.

Ограничение доступа SSH к репозиторию

Если хотите сильнее ограничить ключ, можно прописать в authorized_keys принудительную команду borg serve. Это снижает риск: даже если приватный ключ с рабочего VDS утечёт, им нельзя будет открыть обычную shell-сессию от пользователя backup. Но настройку надо делать внимательно: ошибка в forced command сломает автоматические бэкапы.

Пример строки в authorized_keys на backup-сервере выглядит так. Публичный ключ в конце замените своим содержимым из файла borg_backup_ed25519.pub.

command="borg serve --restrict-to-path /srv/borg/web01",restrict ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAEXAMPLE borg-backup-web01

Опция restrict включает набор ограничений OpenSSH: запрещает port forwarding, X11 forwarding, agent forwarding и другие лишние возможности. --restrict-to-path ограничивает Borg указанным каталогом. После изменения обязательно проверьте создание тестового архива и borg list.

Что исключать из бэкапа и как не забить диск

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

Проверьте крупные каталоги до настройки бэкапа:

sudo du -xhd1 /var | sort -h
sudo du -xhd1 /home | sort -h
sudo du -xhd1 /var/www | sort -h

Для веб-проектов часто исключают node_modules, vendor при наличии lock-файлов и стабильного процесса деплоя, директории cache, preview-изображения и временные uploads. Но не исключайте автоматически то, что нельзя восстановить из Git, пакетного менеджера или исходных данных. Пользовательские uploads почти всегда нужно сохранять, а thumbnails можно пересоздать.

После нескольких запусков оцените размер репозитория:

borg info

Смотрите не только общий размер, но и скорость роста. Если ежедневный прирост слишком большой, изучайте, какие файлы меняются. Режим --list и фильтр --filter AME, которые мы включили в скрипт, помогают увидеть добавленные, изменённые и удалённые элементы.

Отдельный timer для проверки репозитория

Чтобы не смешивать ежедневный backup и тяжёлую проверку, можно сделать отдельный service и timer для borg check. Например, запускать по воскресеньям утром. Это особенно полезно, если репозиторий растёт, а ночное окно ограничено.

sudo nano /etc/systemd/system/borg-web01-check.service
[Unit]
Description=Check BorgBackup repository for web01
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
EnvironmentFile=/etc/borg/web01.env
ExecStart=/usr/bin/borg check --repository-only
ExecStart=/usr/bin/borg check --archives-only --last 7
sudo nano /etc/systemd/system/borg-web01-check.timer
[Unit]
Description=Run BorgBackup check weekly

[Timer]  05:30:00
RandomizedDelaySec=30m
Persistent=true
Unit=borg-web01-check.service

[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now borg-web01-check.timer
systemctl list-timers borg-web01-check.timer

Если проверка начинает занимать слишком много времени, не отключайте её навсегда. Лучше уменьшите частоту, проверяйте последние архивы или перенесите тяжёлые проверки на период низкой нагрузки. Непроверенный репозиторий может годами выглядеть здоровым, пока не наступит день восстановления.

Мониторинг и типичные ошибки

Минимальный мониторинг — это проверка статуса systemd-сервисов и времени последнего успешного запуска timer. На одном сервере достаточно регулярно смотреть журнал, но для production лучше отправлять статус во внешнюю систему мониторинга. Даже простой алерт «бэкап не завершался 36 часов» уже спасает от неприятных сюрпризов.

systemctl status borg-web01-backup.timer
systemctl list-timers --all | grep borg
journalctl -u borg-web01-backup.service --since today --no-pager

Systemd помечает service как failed, если скрипт завершился с ненулевым кодом. Borg возвращает разные коды для успешного завершения, предупреждений и ошибок. Не глушите ошибки через безусловный || true: так легко получить зелёный статус при пустом или неполном архиве.

  • Permission denied publickey. Проверьте путь к ключу, права 600 на приватный ключ, права 700 на .ssh и владельца authorized_keys.
  • Repository does not exist. Репозиторий не инициализирован или путь в BORG_REPO отличается от реального.
  • Failed to create/acquire the lock. Возможно, предыдущий бэкап ещё идёт или завершился аварийно. Сначала убедитесь, что процесса Borg нет.
  • No space left on device. Закончилось место на backup-сервере или на рабочем VDS в каталоге временных дампов.
  • Passphrase wrong. Файл окружения не читается сервисом, переменная названа с ошибкой или пароль действительно другой.

Для диагностики окружения systemd полезно запустить сервис вручную и сразу смотреть журнал:

sudo systemctl reset-failed borg-web01-backup.service
sudo systemctl start borg-web01-backup.service
sudo journalctl -u borg-web01-backup.service -n 300 --no-pager

Короткий чек-лист перед закрытием задачи

Перед тем как забыть о настройке до первого инцидента, пройдитесь по короткому чек-листу. Он занимает немного времени, но резко повышает шанс, что backup действительно поможет.

  1. Первый borg create успешно завершился, архив виден через borg list.
  2. systemd timer включён и показывает следующий запуск.
  3. borg prune настроен под ваш RPO, а не скопирован вслепую.
  4. Дампы баз создаются консистентно и не остаются навсегда в рабочем каталоге.
  5. Из архива восстановлен тестовый файл, а лучше — небольшой тестовый сайт или база.
  6. Passphrase сохранён вне рабочего VDS и доступен ответственным администраторам.
  7. Есть алерт или хотя бы регулярная проверка статуса timer и свежести архивов.

BorgBackup, SSH и systemd timer дают простую и надёжную основу для резервного копирования VDS на Debian/Ubuntu. Здесь нет тяжёлой платформы, веб-панели и сложной агентской схемы, зато есть прозрачные команды, понятные логи и контроль над данными. Начните с малого: один репозиторий, один ежедневный timer, одно тестовое восстановление. Потом добавляйте отдельные проверки, более строгие SSH-ограничения и мониторинг.

Поделиться статьей

Вам будет интересно

Debian/Ubuntu: бэкапы restic, forget/prune и systemd timer OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: бэкапы restic, forget/prune и systemd timer

Разбираем рабочую схему резервного копирования Debian/Ubuntu через restic: установка, репозиторий, первый бэкап, проверка, политик ...
Debian/Ubuntu: почему logrotate не работает и как отладить ротацию через systemd OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: почему logrotate не работает и как отладить ротацию через systemd

Если логи на сервере растут, а ротация молчит, проблема часто не в одном logrotate. В Debian и Ubuntu важны systemd timer, cron, п ...
Debian/Ubuntu: безопасная настройка SSH-ключей на VDS OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: безопасная настройка SSH-ключей на VDS

Безопасно переводим Debian/Ubuntu на вход по SSH-ключам: создаём администратора с sudo, добавляем authorized_keys, проверяем права ...