Бекапи серверів: як робити, перевіряти й відновлювати

Інше 23 жовт. 2026 р.
 Бекапи серверів: як робити, перевіряти й відновлювати

Про бекапи зазвичай згадують тоді, коли вже пізно: диск сервера помер, хтось виконав rm -rf не в тій папці, сайт зламали й зашифрували файли. Неприємна правда в тому, що бекапи є майже в усіх, а от робочі бекапи, з яких реально можна відновитися, — набагато рідше.

У цій статті розберемо все по порядку: що саме копіювати, які бувають види бекапів, якими інструментами їх робити на Linux-сервері, як автоматизувати, як перевіряти, що бекап справний, і як відновлюватися, коли щось пішло не так.

Головне правило: 3-2-1

Класична схема, яка закриває майже всі сценарії втрати даних:

  • 3 копії даних — оригінал і дві резервні;
  • 2 різні носії — наприклад, локальний диск і віддалений сервер або хмара;
  • 1 копія поза основним місцем — в іншому дата-центрі, в іншого провайдера.

Сучасний варіант правила — 3-2-1-1-0: ще одна копія, яку неможливо змінити чи видалити (захист від шифрувальників), і 0 помилок під час перевірки відновлення. Останній пункт найважливіший: бекап, який ніхто ніколи не пробував відновити, — це не бекап, а надія.

Чого бекапом не є

  • RAID. Захищає від смерті одного диска, але не від видалення файлів, злому чи помилки в коді: видалене зникає з усіх дисків масиву одночасно.
  • Синхронізація (Dropbox, Google Drive, rsync --delete). Якщо файл видалено чи зашифровано на сервері, синхронізація слухняно повторить це в копії.
  • Снапшот на тому самому диску чи в того самого провайдера. Чудово для швидкого відкату, але якщо зникне сам сервер чи акаунт, разом із ним зникнуть і снапшоти.

Що саме бекапити

Перш ніж обирати інструмент, складіть список того, без чого сервер не відновити:

  • Бази даних — найцінніше й найбільш мінливе. MySQL/MariaDB, PostgreSQL, Redis (якщо там не лише кеш).
  • Файли сайтів і застосунків — особливо завантажені користувачами (uploads/), бо код зазвичай є в git, а картинок там немає.
  • Конфігурація — /etc (nginx, php, cron, ssh), файли .env, crontab користувачів (crontab -l).
  • Сертифікати й ключі — /etc/letsencrypt, SSH-ключі для деплою.
  • Томи Docker — /var/lib/docker/volumes або дампи баз із контейнерів.
  • Список встановлених пакетів — щоб швидко відтворити систему: dpkg --get-selections > packages.txt.
  • Розмітка дисків і монтування — /etc/fstab, вивід lsblk -f і таблиця розділів. Без них на новому залізі доведеться згадувати, який диск куди був змонтований.

А от /proc, /sys, /tmp, кеші, node_modules, vendor і логи зазвичай копіювати не варто: вони або генеруються автоматично, або не мають цінності, а місця займають багато.

Види бекапів

Повний, інкрементний, диференційний

  • Повний (full) — копія всього щоразу. Найпростіше відновлення, але довго й багато місця.
  • Інкрементний — тільки зміни з моменту попереднього бекапу. Швидко й економно, але для відновлення потрібен ланцюжок: повний + усі інкременти.
  • Диференційний — зміни з моменту останнього повного бекапу. Компроміс: для відновлення треба лише повний + останній диференційний.

Сучасні інструменти на кшталт restic і borg знімають це питання: вони використовують дедуплікацію. Кожен бекап виглядає як повний знімок, але фізично зберігаються лише нові шматки даних. Це дає швидкість інкрементного бекапу й простоту відновлення повного.

Файловий бекап і образ диска

  • Файловий — копіюються файли й папки. Гнучко: можна відновити один файл. Для відновлення всього сервера треба спершу встановити систему.
  • Образ диска / снапшот VPS — копія диска цілком. Відновлення «в один клік», але важко дістати окремий файл, і образ займає багато місця.

Оптимально поєднувати: снапшоти від хостера для швидкого відкату після невдалого оновлення та файлові бекапи на інший сервер для серйозних аварій.

RPO і RTO: скільки можна втратити

Дві метрики, які допомагають вирішити, як часто робити бекапи:

  • RPO (Recovery Point Objective) — скільки даних можна втратити. Бекап раз на добу означає, що в найгіршому випадку зникнуть зміни за добу. Для інтернет-магазину це замовлення за цілий день.
  • RTO (Recovery Time Objective) — скільки часу можна бути недоступним, поки йде відновлення.

Для блогу вистачить щоденного бекапу. Для магазину чи CRM базу варто бекапити щогодини або налаштувати реплікацію.

Бекап баз даних

Копіювати файли бази (/var/lib/mysql) «наживо» не можна: під час копіювання база змінюється, і копія вийде неконсистентною. Правильний спосіб — логічний дамп.

MySQL / MariaDB

# Одна база, стиснута
mysqldump --single-transaction --routines --triggers --events \
  mysite | gzip > /backup/db/mysite-$(date +%F_%H%M).sql.gz

# Усі бази разом
mysqldump --single-transaction --routines --triggers --events \
  --all-databases | gzip > /backup/db/all-$(date +%F).sql.gz

Ключ --single-transaction робить консистентний знімок таблиць InnoDB без блокування сайту. --routines, --triggers і --events додають у дамп збережені процедури, тригери й події, про які часто забувають. На MariaDB 11 утиліта називається mariadb-dump, ключі ті самі.

Щоб не писати пароль у команді (він буде видно в ps та історії), створіть файл ~/.my.cnf з правами 600:

[client]
user=backup
password=надійний_пароль

Для бекапів краще завести окремого користувача бази з мінімальними правами:

CREATE USER 'backup'@'localhost' IDENTIFIED BY 'надійний_пароль';
GRANT SELECT, SHOW VIEW, TRIGGER, LOCK TABLES, EVENT, PROCESS ON *.* TO 'backup'@'localhost';

PostgreSQL

# Одна база у власному стиснутому форматі (рекомендовано)
sudo -u postgres pg_dump -Fc mysite > /backup/db/mysite-$(date +%F).dump

# Усі бази й ролі разом (звичайний SQL)
sudo -u postgres pg_dumpall | gzip > /backup/db/pg-all-$(date +%F).sql.gz

Формат -Fc стиснутий і дозволяє при відновленні вибрати окремі таблиці. Але ролі й користувачів він не містить, їх додатково зберігайте через pg_dumpall --globals-only.

Бази в Docker

docker compose exec -T db sh -c \
  'exec mariadb-dump --single-transaction -uroot -p"$MYSQL_ROOT_PASSWORD" mysite' \
  | gzip > /backup/db/mysite-$(date +%F).sql.gz

Одинарні лапки потрібні, щоб змінна з паролем підставилась усередині контейнера, де вона задана.

Інструменти для файлових бекапів

tar: просто й без залежностей

tar -czf /backup/site-$(date +%F).tar.gz \
  --exclude='*/cache/*' --exclude='*/node_modules' \
  /var/www/mysite /etc/nginx

Плюси: є на кожному сервері, архів відкривається чим завгодно. Мінуси: щоразу повна копія, немає дедуплікації й шифрування, а видаляти старі архіви треба самому:

find /backup -name "site-*.tar.gz" -mtime +14 -delete

rsync: знімки з жорсткими посиланнями

Звичайний rsync — це синхронізація, а не бекап. Але з ключем --link-dest він створює повноцінні щоденні знімки: незмінені файли не копіюються, а стають жорсткими посиланнями на файли з попереднього знімка й місця майже не займають.

#!/bin/bash
DEST=/backup/snapshots
TODAY=$(date +%F)

rsync -a --delete \
  --link-dest="$DEST/latest" \
  /var/www/ "$DEST/$TODAY/"

ln -sfn "$DEST/$TODAY" "$DEST/latest"

Кожна папка з датою виглядає як повна копія, і відновити файл можна звичайним cp. Мінус: немає шифрування, тому для віддаленого зберігання краще підходять наступні інструменти.

restic: сучасний вибір

restic — один бінарник, шифрування за замовчуванням, дедуплікація, і з коробки вміє зберігати бекапи локально, по SFTP, в S3, Backblaze B2 та інших сховищах (а через rclone — майже будь-куди).

sudo apt install restic

# Налаштування через змінні середовища
export RESTIC_REPOSITORY="sftp:backup@backup-server:/srv/restic/myserver"
export RESTIC_PASSWORD_FILE=/root/.restic-password

# Створити репозиторій (один раз)
restic init

# Зробити бекап
restic backup /var/www /etc /backup/db \
  --exclude="*/cache/*" --exclude="node_modules"

# Подивитися список знімків
restic snapshots

Збережіть пароль від репозиторію окремо від сервера (у менеджері паролів). Без нього відновити бекап неможливо в принципі — це плата за шифрування.

Політика зберігання: скільки знімків лишати.

restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune

Ця команда лишить 7 останніх щоденних знімків, 4 тижневих і 6 місячних, а решту видалить разом з даними, які більше ніде не використовуються.

restic + rclone: бекап у Google Drive, OneDrive, Dropbox та інші хмари

Сам restic підтримує обмежений набір сховищ: локальну папку, SFTP, S3, B2, Azure, Google Cloud Storage. Але часто під рукою є інше: 2 ТБ на Google Drive, OneDrive з Microsoft 365, Dropbox, pCloud чи Mega. Тут на допомогу приходить rclone — утиліта, яка вміє працювати з понад 70 хмарними сховищами. restic уміє використовувати rclone як транспорт: він сам запускає rclone у фоні й передає через нього дані.

Розподіл ролей простий: restic відповідає за дедуплікацію, шифрування й знімки, а rclone — лише за доставку файлів у хмару. Хмара бачить тільки зашифровані блоки, тож навіть провайдер не може прочитати ваші дані.

1. Встановити rclone

Версія в репозиторіях дистрибутива часто застаріла, краще ставити офіційним скриптом:

sudo -v ; curl https://rclone.org/install.sh | sudo bash
rclone version

2. Підключити хмару

Налаштування інтерактивне:

sudo rclone config

Далі: n (новий remote) → ім'я, наприклад gdrive → тип сховища (drive для Google Drive, onedrive, dropbox тощо) → решту параметрів можна лишити за замовчуванням.

Запускайте rclone config від того користувача, від якого працюватиме бекап (зазвичай root). Налаштування зберігаються у ~/.config/rclone/rclone.conf цього користувача, і restic, запущений від root, шукатиме їх у /root/.config/rclone/.

На сервері без браузера Google Drive, OneDrive і Dropbox потребують входу через OAuth. На питання «Use web browser to automatically authenticate?» відповідайте n. rclone покаже команду, яку треба виконати на комп'ютері з браузером (там теж має бути встановлений rclone):

# на своєму комп'ютері
rclone authorize "drive"

Відкриється браузер, ви ввійдете в акаунт, а rclone виведе токен. Скопіюйте його й вставте в сесію rclone config на сервері.

Перевіряємо, що доступ є:

sudo rclone lsd gdrive:          # список папок у корені
sudo rclone about gdrive:        # скільки місця зайнято й вільно
sudo rclone mkdir gdrive:backups

3. Створити репозиторій restic через rclone

Адреса репозиторію має вигляд rclone:<ім'я remote>:<шлях>:

export RESTIC_REPOSITORY="rclone:gdrive:backups/myserver"
export RESTIC_PASSWORD_FILE=/root/.restic-password

restic init
restic backup /var/www /etc /backup/db
restic snapshots

Усе інше працює так само, як з будь-яким іншим сховищем: forget, prune, check, restore, mount. У скрипті автоматизації нижче досить замінити один рядок з RESTIC_REPOSITORY.

4. Тонке налаштування

Додаткові параметри для rclone передаються через опцію -o rclone.args. Наприклад, для Google Drive корисно вимкнути переміщення видаленого в кошик (інакше після кожного prune кошик заповнюється старими блоками й займає місце) і обмежити швидкість, щоб бекап не забивав канал сервера:

restic -o rclone.args="serve restic --stdio --drive-use-trash=false --bwlimit 20M" \
  backup /var/www

Початок serve restic --stdio обов'язковий: це команда, якою restic запускає rclone, і без неї з'єднання не встановиться.

Ще кілька порад для хмарних дисків:

  • Власний client_id для Google Drive. Стандартний ключ rclone спільний для всіх користувачів і часто впирається в ліміти запитів. Створіть свій у Google Cloud Console (інструкція є в документації rclone) і вкажіть його під час rclone config.
  • Перший бекап довгий. Хмарні диски повільніші за S3, тож первинне завантаження десятків гігабайт може зайняти години. Наступні бекапи передають лише зміни й проходять швидко.
  • Ліміти завантаження. Google Drive дозволяє завантажити близько 750 ГБ на добу на акаунт. Для звичайного сервера цього з запасом, але про ліміт варто знати під час першого бекапу великого обсягу.

5. Захистити конфіг rclone

У rclone.conf зберігаються токени доступу до вашого хмарного акаунта. Хто отримає цей файл, отримає доступ до всього диска, а не лише до бекапів. Мінімум — права 600. Краще — зашифрувати конфіг паролем через меню rclone config → s) Set configuration password, а пароль передавати скрипту через змінну:

export RCLONE_CONFIG_PASS="$(cat /root/.rclone-config-pass)"

Для бекапів також варто завести окремий хмарний акаунт, а не використовувати особистий: якщо токен витече, постраждають лише бекапи.

6. Друга копія в хмару (3-2-1)

Типова схема: основний репозиторій на локальному диску чи сусідньому сервері (швидко робити й відновлювати), а копія в хмарі (на випадок, якщо зникне все). Найкраще копіювати знімки засобами самого restic:

# Створити хмарний репозиторій з тими самими параметрами розбиття,
# щоб дедуплікація працювала між репозиторіями
restic -r rclone:gdrive:backups/myserver init \
  --from-repo /mnt/backup/restic --copy-chunker-params \
  --from-password-file /root/.restic-password

# Після кожного локального бекапу копіювати нові знімки в хмару
restic -r rclone:gdrive:backups/myserver copy \
  --from-repo /mnt/backup/restic \
  --from-password-file /root/.restic-password

restic copy переносить лише нові знімки й перевіряє дані, а хмарний репозиторій лишається самостійним: його можна перевірити й відновити з нього окремо. Політику зберігання (forget --prune) запускайте для кожного репозиторію окремо.

Простіший варіант — синхронізувати папку репозиторію через сам rclone:

rclone sync /mnt/backup/restic gdrive:backups/restic-mirror \
  --backup-dir gdrive:backups/restic-deleted/$(date +%F)

Але пам'ятайте про недолік будь-якої синхронізації: якщо локальний репозиторій пошкодиться чи його зашифрує вірус, sync перенесе пошкодження в хмару. Ключ --backup-dir частково рятує: видалені й змінені файли не зникають, а переносяться в окрему папку з датою.

7. Відновлення з хмари на новому сервері

На новому сервері конфігу rclone немає, тому:

# 1. Встановити restic і rclone
sudo apt install restic
sudo -v ; curl https://rclone.org/install.sh | sudo bash

# 2. Повернути rclone.conf із надійного місця (або заново пройти rclone config)
sudo mkdir -p /root/.config/rclone
sudo cp rclone.conf /root/.config/rclone/
sudo chmod 600 /root/.config/rclone/rclone.conf

# 3. Відновити дані
sudo restic -r rclone:gdrive:backups/myserver snapshots
sudo restic -r rclone:gdrive:backups/myserver restore latest --target /restore

Тому разом з паролем від restic зберігайте окремо від сервера (наприклад, у менеджері паролів) і rclone.conf або хоча б інструкцію, як заново підключити хмару. Без них хмарна копія недоступна.

BorgBackup: альтернатива

Borg працює за схожим принципом: дедуплікація, стиснення, шифрування. Добре стискає дані й має великий досвід використання, але для віддаленого репозиторію потребує borg і на сервері-сховищі (або сервісів на кшталт BorgBase чи Hetzner Storage Box).

borg init --encryption=repokey backup@backup-server:/srv/borg/myserver

borg create --stats --compression zstd \
  backup@backup-server:/srv/borg/myserver::'{hostname}-{now:%Y-%m-%d}' \
  /var/www /etc /backup/db

borg prune --keep-daily=7 --keep-weekly=4 --keep-monthly=6 \
  backup@backup-server:/srv/borg/myserver
borg compact backup@backup-server:/srv/borg/myserver

З режимом repokey ключ шифрування зберігається в репозиторії під паролем. Зробіть його резервну копію командою borg key export і тримайте окремо.

Снапшоти VPS і файлових систем

  • Снапшоти хостера (Hetzner, DigitalOcean, AWS) — найпростіше для повного відкату сервера. Робіть знімок перед кожним великим оновленням системи.
  • LVM, ZFS, Btrfs — миттєві знімки файлової системи. Зручно зробити знімок, з нього спокійно скопіювати консистентні дані й видалити знімок.
  • Timeshift — для робочих машин на Linux: знімки системи, щоб відкотитися після невдалого оновлення. Особисті файли він за замовчуванням не бекапить.

Диски й монтування для бекапів

Бекап на тому самому диску, що й дані, захищає лише від випадкового видалення. Якщо помре диск, зникне все. Тому сховище для бекапів — це окремий диск, мережева папка або віддалений сервер, і їх треба правильно підключити.

Підключити новий диск

Спершу подивимось, які диски бачить система:

lsblk -f
# NAME   FSTYPE LABEL  UUID                                 MOUNTPOINTS
# sda
# ├─sda1 ext4          3f6c...                              /
# sdb                                                       <- новий порожній диск

Новий диск без розділів і файлової системи. Створюємо розділ на весь диск і форматуємо його. Тричі перевірте назву диска: ці команди знищують усі дані на ньому.

sudo parted /dev/sdb --script mklabel gpt mkpart primary ext4 0% 100%
sudo mkfs.ext4 -L backup /dev/sdb1

Монтуємо вручну, щоб перевірити:

sudo mkdir -p /mnt/backup
sudo mount /dev/sdb1 /mnt/backup
df -h /mnt/backup

Постійне монтування через fstab

Щоб диск підключався після перезавантаження, додаємо його в /etc/fstab. Використовуйте UUID, а не /dev/sdb1: назви дисків можуть змінитися після перезавантаження чи підключення іншого диска, а UUID лишається тим самим.

# Дізнатися UUID розділу
sudo blkid /dev/sdb1
# /dev/sdb1: LABEL="backup" UUID="9b2e1c4a-..." TYPE="ext4"

Рядок у /etc/fstab:

UUID=9b2e1c4a-...  /mnt/backup  ext4  defaults,noatime,nofail  0  2

Опція nofail обов'язкова для дисків з бекапами: без неї, якщо диск помре або його відключать, сервер не завантажиться і зупиниться в аварійному режимі. З nofail система просто запуститься без цього диска.

Перевірити fstab до перезавантаження:

sudo findmnt --verify   # синтаксис і наявність пристроїв
sudo mount -a           # змонтувати все з fstab

Помилка в fstab — одна з найчастіших причин, через які сервер не піднімається після перезавантаження. Ніколи не перезавантажуйтеся, не перевіривши його цими командами.

Монтувати диск лише на час бекапу

Диск, змонтований постійно, так само вразливий, як і основний: шифрувальник чи помилкова команда зачепить і його. Надійніше підключати його лише під час бекапу. Для цього в fstab додаємо noauto:

UUID=9b2e1c4a-...  /mnt/backup  ext4  noauto,noatime,nofail  0  0

А в скрипті бекапу:

mount /mnt/backup
# ... бекап ...
umount /mnt/backup

Мережеві сховища: NFS, SMB, SSHFS

NFS — стандарт для Linux-серверів і NAS:

sudo apt install nfs-common
sudo mount -t nfs 192.168.1.10:/export/backup /mnt/nfs

# fstab
192.168.1.10:/export/backup  /mnt/nfs  nfs  defaults,_netdev,nofail  0  0

SMB / CIFS — для NAS (Synology, QNAP), спільних папок Windows і сховищ на кшталт Hetzner Storage Box:

sudo apt install cifs-utils

# Файл з доступами, щоб пароль не лежав у fstab
sudo tee /root/.smbcredentials > /dev/null <<'EOF'
username=backup
password=надійний_пароль
EOF
sudo chmod 600 /root/.smbcredentials

# fstab
//nas.local/backup  /mnt/nas  cifs  credentials=/root/.smbcredentials,uid=0,gid=0,iocharset=utf8,_netdev,nofail  0  0

SSHFS — змонтувати папку будь-якого сервера, до якого є SSH, без налаштування сервера:

sudo apt install sshfs
sudo sshfs backup@backup-server:/srv/backup /mnt/remote \
  -o reconnect,IdentityFile=/root/.ssh/id_backup

# відмонтувати
sudo umount /mnt/remote

Опція _netdev у fstab каже системі, що це мережевий диск і монтувати його треба після запуску мережі. Без неї завантаження може зависнути в очікуванні.

Порада: restic і borg уміють працювати з SFTP напряму, без монтування. Мережеве монтування зручне для tar і rsync або коли NAS доступний лише через SMB чи NFS.

Перевіряйте, що диск справді змонтовано

Класична пастка: диск з бекапами відвалився, папка /mnt/backup стала звичайною порожньою папкою на системному диску, і скрипт почав писати бекапи туди. Через кілька днів системний диск заповнюється, сервер падає, а бекапів на справжньому диску немає. Захист — одна перевірка на початку скрипта:

if ! mountpoint -q /mnt/backup; then
    echo "Диск /mnt/backup не змонтовано, бекап скасовано" >&2
    exit 1
fi

Додатковий трюк: зробіть точку монтування недоступною для запису, поки диск не підключено. Тоді навіть без перевірки запис у неї завершиться помилкою:

sudo umount /mnt/backup
sudo chattr +i /mnt/backup      # порожню папку неможливо змінити
sudo mount /mnt/backup          # змонтований диск працює як звичайно

Знімки LVM і Btrfs для консистентного бекапу

Якщо файли змінюються під час копіювання, в бекап потрапить суміш старих і нових версій. Знімок файлової системи фіксує стан на одну мить, і копіювати можна вже з нього.

LVM (якщо дані лежать на логічному томі):

# Створити знімок тому з 5 ГБ місця під зміни
sudo lvcreate --size 5G --snapshot --name data_snap /dev/vg0/data

# Змонтувати знімок тільки для читання (для XFS додайте nouuid)
sudo mkdir -p /mnt/snap
sudo mount -o ro /dev/vg0/data_snap /mnt/snap

# Бекап зі знімка
restic backup /mnt/snap

# Прибрати знімок
sudo umount /mnt/snap
sudo lvremove -y /dev/vg0/data_snap

Розмір знімка — це скільки змін він може вмістити, поки існує. Якщо за час бекапу даних зміниться більше, знімок стане непридатним, тому не тримайте його довше, ніж потрібно.

Btrfs — знімки миттєві й майже нічого не займають:

sudo btrfs subvolume snapshot -r /data /data/.snapshots/data-$(date +%F)
restic backup /data/.snapshots/data-$(date +%F)
sudo btrfs subvolume delete /data/.snapshots/data-$(date +%F)

Образ цілого диска

Іноді потрібна побітова копія всього диска: наприклад, перед заміною диска чи щоб зберегти систему з нестандартним завантажувачем. Диск при цьому не має бути змонтований (робиться з Live USB або для другорядного диска):

sudo dd if=/dev/sdb bs=4M status=progress conv=fsync | gzip > /mnt/backup/sdb.img.gz

Щоб дістати файли з образу, не розгортаючи його на диск, його можна змонтувати:

gunzip -k /mnt/backup/sdb.img.gz

# Підключити образ як віртуальний диск з розділами
sudo losetup -fP --show /mnt/backup/sdb.img
# /dev/loop0  -> розділи з'являться як /dev/loop0p1, /dev/loop0p2 ...

sudo mount -o ro /dev/loop0p1 /mnt/img
ls /mnt/img

# Відключити
sudo umount /mnt/img
sudo losetup -d /dev/loop0

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

Зберегти розмітку дисків

Ці кілька файлів важать кілобайти, але дуже допоможуть, коли доведеться відтворювати сервер. Додайте їх у скрипт бекапу:

cp /etc/fstab "$DB_DIR/fstab"
lsblk -f > "$DB_DIR/lsblk.txt"
sudo sfdisk -d /dev/sda > "$DB_DIR/sda.partitions"

Що обрати

  • Один невеликий сайт, бекап на той самий або сусідній сервер — tar або rsync + дамп бази.
  • Кілька сайтів, бекап у хмару чи на інший сервер, потрібне шифрування — restic.
  • Є місце на Google Drive, OneDrive чи Dropbox і хочеться використати його під бекапи — restic + rclone.
  • Є свій сервер для бекапів, важлива максимальна компресія — borg.
  • Додатково, для швидкого відкату — снапшоти хостера.

Автоматизація: повний скрипт

Бекап, який треба запускати вручну, рано чи пізно перестануть запускати. Ось скрипт, що робить дамп бази, бекап через restic, чистку старих знімків і повідомляє про результат:

#!/bin/bash
# /usr/local/bin/backup.sh
set -euo pipefail

export RESTIC_REPOSITORY="sftp:backup@backup-server:/srv/restic/myserver"
export RESTIC_PASSWORD_FILE=/root/.restic-password
DB_DIR=/backup/db
HC_URL="https://hc-ping.com/ваш-uuid"   # моніторинг, див. нижче

# Якщо будь-яка команда впаде — повідомити про помилку
trap 'curl -fsS -m 10 --retry 3 "$HC_URL/fail" > /dev/null' ERR

curl -fsS -m 10 --retry 3 "$HC_URL/start" > /dev/null

# 1. Дампи баз
mkdir -p "$DB_DIR"
mysqldump --single-transaction --routines --triggers --events \
  --all-databases | gzip > "$DB_DIR/all.sql.gz"
dpkg --get-selections > "$DB_DIR/packages.txt"
crontab -l > "$DB_DIR/crontab.txt" 2>/dev/null || true
lsblk -f > "$DB_DIR/lsblk.txt"
sfdisk -d /dev/sda > "$DB_DIR/sda.partitions" 2>/dev/null || true

# 2. Бекап файлів
restic backup /var/www /etc /root "$DB_DIR" \
  --exclude="*/cache/*" --exclude="node_modules" --exclude="*.log"

# 3. Політика зберігання
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune

# 4. Швидка перевірка цілісності репозиторію
restic check

curl -fsS -m 10 --retry 3 "$HC_URL" > /dev/null

Якщо репозиторій лежить на змонтованому диску (наприклад, RESTIC_REPOSITORY=/mnt/backup/restic), додайте на початок скрипта перевірку mountpoint -q /mnt/backup з розділу про диски.

Зверніть увагу: дамп бази перезаписується в той самий файл all.sql.gz. Історію версій зберігає restic, тож окремі файли з датами не потрібні, а дедуплікація не страждає.

Запуск щоночі о 3:00 через cron (sudo crontab -e):

0 3 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1

Моніторинг: дізнатися, що бекап не зробився

Найпідступніша ситуація — бекап тихо перестав працювати пів року тому, а ви дізналися про це під час аварії. Тому потрібне сповіщення не лише про помилку, а й про те, що бекап не запустився взагалі.

Для цього ідеально підходить Healthchecks.io (є безкоштовний тариф і можна підняти свій екземпляр): скрипт «пінгує» унікальну адресу після успішного бекапу. Якщо пінга немає довше, ніж очікувалось, ви отримуєте лист або повідомлення в Telegram. Саме це роблять рядки з curl у скрипті вище.

Як перевіряти бекапи

Перевірка буває трьох рівнів, і робити варто всі три.

Рівень 1: бекап зробився і файли цілі

# Архів не пошкоджено
gzip -t /backup/db/all.sql.gz && echo "OK"
tar -tzf /backup/site-2026-10-23.tar.gz > /dev/null && echo "OK"

# Дамп MySQL записано до кінця (останній рядок має бути "Dump completed")
zcat /backup/db/all.sql.gz | tail -n 1

# Дамп PostgreSQL читається
pg_restore -l /backup/db/mysite.dump > /dev/null && echo "OK"

# Розмір не впав різко (порожній дамп — ознака проблеми)
ls -lh /backup/db/

Рівень 2: цілісність репозиторію

# restic: перевірити структуру репозиторію
restic check

# раз на тиждень — ще й прочитати частину даних
restic check --read-data-subset=10%

# borg
borg check backup@backup-server:/srv/borg/myserver

Звичайний restic check перевіряє метадані й структуру. З ключем --read-data-subset він ще й реально читає й перевіряє частину даних, тож за кілька тижнів можна перевірити весь репозиторій.

Рівень 3: тестове відновлення

Єдина перевірка, яка дає справжню впевненість. Раз на місяць (поставте собі нагадування) відновлюйте бекап на окремий сервер або в Docker-контейнер і перевіряйте, що все працює:

# Підняти тимчасову базу й залити в неї дамп
docker run -d --name restore-test -e MARIADB_ROOT_PASSWORD=test mariadb:11
sleep 20
zcat /backup/db/all.sql.gz | docker exec -i restore-test mariadb -uroot -ptest

# Перевірити, що дані на місці
docker exec restore-test mariadb -uroot -ptest \
  -e "SELECT COUNT(*) FROM mysite.orders;"

docker rm -f restore-test

Під час тестового відновлення засікайте час. Так ви дізнаєтесь свій реальний RTO і знайдете проблеми заздалегідь: забутий конфіг, відсутній пароль, неправильні права на файли.

Як відновлювати

Один файл або папку

# restic: знайти файл у знімках
restic find "wp-config.php"

# відновити папку з останнього знімка в тимчасове місце
restic restore latest --target /tmp/restore --include /var/www/mysite/uploads

# або змонтувати всі знімки як папки й скопіювати потрібне
mkdir -p /mnt/restic
restic mount /mnt/restic
# у сусідньому терміналі: ls /mnt/restic/snapshots/
# borg: розпаковує в поточну папку!
cd /tmp/restore
borg list backup@backup-server:/srv/borg/myserver
borg extract backup@backup-server:/srv/borg/myserver::myserver-2026-10-23 var/www/mysite

Відновлюйте спершу в тимчасову папку, а не одразу поверх робочих файлів. Порівняйте (diff -r), переконайтеся, що це та версія, і лише потім копіюйте на місце.

Базу даних

# MySQL / MariaDB: одна база
gunzip < /tmp/restore/mysite.sql.gz | mysql mysite

# PostgreSQL з формату -Fc (--clean видалить наявні об'єкти перед відновленням)
sudo -u postgres pg_restore --clean --if-exists -d mysite /tmp/restore/mysite.dump

# Тільки одна таблиця з PostgreSQL-дампу
sudo -u postgres pg_restore -d mysite -t orders /tmp/restore/mysite.dump

Перед відновленням бази поверх робочої зробіть дамп поточного стану, навіть якщо він пошкоджений. Якщо щось піде не так, буде куди повернутися, а з пошкодженого дампу іноді вдається витягти дані, яких немає в бекапі.

Якщо потрібна одна таблиця з великого дампу MySQL, простіше відновити весь дамп у тимчасову базу й перенести звідти потрібну таблицю:

mysql -e "CREATE DATABASE restore_tmp"
gunzip < mysite.sql.gz | mysql restore_tmp
mysqldump restore_tmp orders | mysql mysite
mysql -e "DROP DATABASE restore_tmp"

Цей приклад працює для дампу однієї бази. Дамп з --all-databases містить команди USE і відновлюється у свої бази з початковими назвами, тому з нього так зробити не вийде.

Весь сервер після аварії

Найгірший сценарій: сервера немає. Порядок дій:

  1. Створити новий сервер із тією самою версією ОС.
  2. Встановити restic або borg і отримати доступ до репозиторію (ось тут знадобиться пароль, збережений не на старому сервері). Якщо бекапи лежать у хмарі через rclone, поверніть також rclone.conf.
  3. Відновити бекап у тимчасову папку: restic restore latest --target /restore.
  4. Встановити пакети за збереженим списком:
    sudo dpkg --set-selections < /restore/backup/db/packages.txt && sudo apt-get dselect-upgrade
  5. Повернути конфіги з /restore/etc. Не копіюйте /etc цілком поверх нової системи: там є файли, прив'язані до конкретного сервера (fstab, мережа, hostname, ключі SSH-сервера). Переносьте вибірково: nginx, php, letsencrypt, cron.
  6. Підключити диски. Якщо потрібна та сама розмітка, відтворіть її зі збереженого файлу: sudo sfdisk /dev/sdb < /restore/backup/db/sda.partitions (лише на новому порожньому диску!). Після форматування в розділів будуть нові UUID, тому старий fstab не підійде як є: дізнайтеся нові UUID через blkid, виправте рядки й перевірте findmnt --verify та mount -a.
  7. Відновити файли сайтів у /var/www і виставити правильного власника: chown -R www-data:www-data /var/www.
  8. Відновити бази з дампів.
  9. Перевірити конфіги (nginx -t), запустити сервіси, перевірити сайти.
  10. Змінити DNS на новий IP.

Запишіть ці кроки для свого сервера в окремий документ (і збережіть його не на сервері). У стресовій ситуації під час аварії він зекономить години.

Безпека бекапів

  • Шифрування. Бекап містить паролі з .env, дані користувачів і ключі. Якщо він лежить у хмарі чи на чужому сервері, він має бути зашифрований. restic і borg роблять це за замовчуванням.
  • Захист від видалення. Якщо зловмисник отримав root на сервері, він бачить і доступи до бекапів, тож може видалити їх разом із даними. Захист: сховище в режимі «тільки дописування» (rest-server --append-only для restic, borg serve --append-only для borg) або S3 з Object Lock. Видаляти старі знімки в такому разі має інший, довірений сервер.
  • Pull замість push. Інший варіант: не сервер відправляє бекапи, а сервер-сховище сам забирає дані. Тоді основний сервер узагалі не має доступу до сховища.
  • Права доступу. Скрипти й файли з паролями — тільки для root: chmod 700 backup.sh, chmod 600 .restic-password.

Чекліст

  • Складено список того, що треба бекапити: бази, uploads, конфіги, сертифікати, томи Docker.
  • Бази бекапляться дампом, а не копіюванням файлів.
  • Принаймні одна копія лежить в іншого провайдера чи в іншому дата-центрі.
  • Диск для бекапів підключено через UUID з опцією nofail, а скрипт перевіряє mountpoint перед записом.
  • Збережено fstab, lsblk -f і таблицю розділів.
  • Бекапи зашифровані, пароль від них (і rclone.conf, якщо використовується) зберігається окремо від сервера.
  • Бекап запускається автоматично за розкладом.
  • Налаштовано політику зберігання: старі знімки видаляються автоматично.
  • Є моніторинг, який повідомить, якщо бекап не зробився.
  • Щотижня перевіряється цілісність репозиторію.
  • Щомісяця робиться тестове відновлення.
  • Записано покроковий план відновлення всього сервера.

Висновок

Бекап складається з трьох частин: зробити копію, перевірити її й уміти з неї відновитися. Більшість зупиняється на першій. Налаштуйте дампи баз і restic або borg на віддалене сховище, додайте моніторинг і раз на місяць пробуйте відновлення. Це кілька годин на налаштування, які одного дня зекономлять дні роботи, а можливо, і бізнес.

Якщо ваші сервіси працюють у Docker, подивіться також розділ про бекапи томів у статті Docker для веброзробника: від першого запуску до продакшну, а базові команди для роботи на сервері зібрані в шпаргалці Linux команди на кожен день.