← До всіх записів
backuprestoreops

Бекапи перестали бути теорією

Зібрав робочий backup pipeline для nginx, Let’s Encrypt, /var/www, MariaDB і Docker volumes, а також автоматизував latest symlink, журнал запусків і restore flow.

15 квітня 20268 хв читанняЄ робочий шлях до відновлення

Бекапи перестали бути теорією

До цього моменту слово "backup" у мене більше означало намір, ніж процес. Сервер уже виглядав акуратно: nginx, сертифікати, Docker-сервіси, сайт, база. Але я в якийсь момент сам собі поставив просте питання: якщо VPS зламається сьогодні ввечері, чи зможу я завтра зранку підняти все назад без нервів і шаманства?

Після цього етапу відповідь уже не "мабуть так", а "так, ось команди, ось архіви, ось шлях відновлення".

Що саме я вирішив архівувати

Перший робочий набір вийшов таким:

  • /etc/nginx
  • /etc/letsencrypt
  • /var/www
  • дамп усіх баз MariaDB
  • Docker volume uptime-kuma
  • Docker volume portainer_data

По-простому: я зібрав усе, без чого було б боляче відновлювати сервер. Це не якийсь enterprise monster, але для першого production baseline набір дуже правильний: конфіги, TLS, контент, база і дані контейнерів.

Де все лежить

Я зробив окрему структуру під серверні бекапи:

terminal
/home/ubuntu/backups/server/

Кожен запуск створює директорію з таймстампом на кшталт:

terminal
/home/ubuntu/backups/server/2026-04-15-1139

Всередині структура розбита по змісту:

  • system/
  • web/
  • db/
  • docker-volumes/

Такий поділ дуже допомагає під час restore: не треба згадувати, де саме лежить nginx, а де mariadb-all.sql.gz.

Команди, якими я запускав і перевіряв перший backup

Після того як скрипт уже з'явився на сервері, я перевіряв його руками:

terminal
chmod +x /home/ubuntu/scripts/backup.sh
/home/ubuntu/scripts/backup.sh
find /home/ubuntu/backups/server -maxdepth 3 -type f | sort

У результаті отримав повний набір архівів:

terminal
/home/ubuntu/backups/server/2026-04-15-1139/db/mariadb-all.sql.gz
/home/ubuntu/backups/server/2026-04-15-1139/docker-volumes/portainer_data.tar.gz
/home/ubuntu/backups/server/2026-04-15-1139/docker-volumes/uptime-kuma.tar.gz
/home/ubuntu/backups/server/2026-04-15-1139/system/etc-letsencrypt.tar.gz
/home/ubuntu/backups/server/2026-04-15-1139/system/etc-nginx.tar.gz
/home/ubuntu/backups/server/2026-04-15-1139/web/var-www.tar.gz

Після цього я не зупинився на самому факті створення файлів і перевірив, що вони взагалі читаються:

terminal
tar -tzf /home/ubuntu/backups/server/2026-04-15-1139/system/etc-nginx.tar.gz | head
tar -tzf /home/ubuntu/backups/server/2026-04-15-1139/system/etc-letsencrypt.tar.gz | head
tar -tzf /home/ubuntu/backups/server/2026-04-15-1139/web/var-www.tar.gz | head
gzip -t /home/ubuntu/backups/server/2026-04-15-1139/db/mariadb-all.sql.gz && echo "MariaDB dump OK"
tar -tzf /home/ubuntu/backups/server/2026-04-15-1139/docker-volumes/uptime-kuma.tar.gz | head
tar -tzf /home/ubuntu/backups/server/2026-04-15-1139/docker-volumes/portainer_data.tar.gz | head

Оце і є той маленький, але дуже дорослий DevOps-момент: backup вважається успішним не тоді, коли архів просто з'явився, а тоді, коли ти його реально відкрив і перевірив.

Найсильніше покращення: latest і LAST_SUCCESSFUL_BACKUP

Одна з найкорисніших змін у скрипті була не в архівації як такій, а в тому, як я зафіксував "останній валідний запуск".

Я додав:

terminal
ln -sfn "$RUN_DIR" "$BACKUP_ROOT/latest"
printf '%s\n' "$RUN_DIR" > "$LATEST_FILE"

Після цього з'явився стабільний маршрут:

terminal
/home/ubuntu/backups/server/latest

і окремий файл:

terminal
/home/ubuntu/backups/server/LAST_SUCCESSFUL_BACKUP

Практична користь колосальна:

  • RESTORE_CHECKLIST.md більше не прив'язаний до конкретної дати
  • не треба вручну редагувати шлях після кожного бекапу
  • легше автоматизувати перевірки
  • менше шансів помилитися під час інциденту

Як виглядає restore checklist після автоматизації

Після доробки RESTORE_CHECKLIST.md вже не вказує на конкретний каталог, а працює через latest.

Ключові restore-команди стали такими:

terminal
sudo tar -xzf /home/ubuntu/backups/server/latest/system/etc-nginx.tar.gz -C /
sudo tar -xzf /home/ubuntu/backups/server/latest/system/etc-letsencrypt.tar.gz -C /
sudo tar -xzf /home/ubuntu/backups/server/latest/web/var-www.tar.gz -C /
gunzip -c /home/ubuntu/backups/server/latest/db/mariadb-all.sql.gz | sudo mysql
docker run --rm -v uptime-kuma:/target -v /home/ubuntu/backups/server/latest/docker-volumes:/backup alpine:3.20 sh -c "cd /target && tar -xzf /backup/uptime-kuma.tar.gz"
docker run --rm -v portainer_data:/target -v /home/ubuntu/backups/server/latest/docker-volumes:/backup alpine:3.20 sh -c "cd /target && tar -xzf /backup/portainer_data.tar.gz"

І окремо команди швидкої перевірки:

terminal
readlink -f /home/ubuntu/backups/server/latest
cat /home/ubuntu/backups/server/LAST_SUCCESSFUL_BACKUP
sudo nginx -t
docker ps
curl -I https://dev.bombaworkflows.com
curl -I https://kuma.bombaworkflows.com
curl -I https://portainer.bombaworkflows.com

Cron і журнал запусків

Щоб backup не залежав від настрою або пам'яті, я поставив щоденний запуск через cron:

terminal
15 3 * * * /home/ubuntu/scripts/backup.sh >> /home/ubuntu/backups/server/backup.log 2>&1

Перевірка виглядала так:

terminal
crontab -l
systemctl status cron --no-pager
/home/ubuntu/scripts/backup.sh >> /home/ubuntu/backups/server/backup.log 2>&1
tail -n 30 /home/ubuntu/backups/server/backup.log

Це маленька деталь, але вона додає прозорості:

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

Що я виніс з цього етапу

Цей крок навчив мене дуже простої думки: інфраструктура починає дорослішати в той момент, коли в неї з'являється шлях назад.

Не зелений статус у дашборді. Не красивий reverse proxy. Не навіть HTTPS.

А саме відтворюваний шлях відновлення.

Висновок

Після цього кроку сервер уже не просто "працює". У нього є:

  • backup script
  • ротація
  • latest symlink
  • лог запусків
  • документований restore flow
  • перевірені архіви

Для першого DevOps-сервера це вже дуже сильний фундамент. І найприємніше тут те, що це не абстрактна стаття "як могло б бути", а наш реальний шлях з живими командами і живими перевірками на VPS.