Бекапи і відновлення
Що робиться саме
Section titled “Що робиться саме”Налаштовувати cron чи скрипти не треба — сайт робить резервні копії сам:
- щоночі о 00:00 за Києвом (час, увімкнення і кількість архівів адміністратор змінює в адмін-панелі: «Налаштування» → «Бекапи»);
- одразу після запуску, якщо останній бекап старший за добу, завершився помилкою або його ще не було;
- новий архів з’являється, лише коли з минулого бекапу на сайті щось змінилося.
Архів — один файл .tar.gz: знімок бази даних і всі завантажені файли. Архіви лежать на сервері в
/opt/tabula/backups, їх назва починається з SITE_SLUG (наприклад,
school-12-backup-20260926-000000.tar.gz). Зберігаються останні 5 (налаштовується від 1 до 30).
Стан бекапів видно в адмін-панелі на сторінці «Бекапи» і в повній відповіді
https://school.example.ua/health (блок "backup"; її бачить лише той, хто надішле заголовок
Authorization: Bearer <HEALTH_TOKEN>, — див. крок 3 встановлення).
Дашборд адміністратора попереджає, якщо бекап не запускався понад 48 годин або
впав. Якщо на диску лишилося менше 1 ГБ, бекап не починається й записується як невдалий з
поясненням — так брак місця видно на тій самій сторінці, а не лише в цифрах /health.
Моніторинг
Section titled “Моніторинг”Щоб дізнатися, що сайт не відповідає чи сертифікат не продовжився, раніше, ніж про це скажуть
відвідувачі, підключіть безкоштовний зовнішній монітор (UptimeRobot, Better Stack тощо):
перевірка адреси https://school.example.ua/health кожні 5–10 хвилин з ключовим словом
"status":"ok" і сповіщення про строк дії SSL-сертифіката. Листи надходять на вашу пошту.
Якщо сайт розгортається з власного репозиторію на GitHub через GitHub Actions, перевірку можна
доручити й йому — див. DEPLOYMENT.md, розділ «Моніторинг».
Копія поза сервером (дуже бажано)
Section titled “Копія поза сервером (дуже бажано)”Архіви в /opt/tabula/backups лежать на тому самому диску, що й сайт. Якщо сервер зламається
чи зникне, разом із ним зникнуть і вони. Тому налаштуйте копію в зовнішнє сховище.
Підходить будь-яке S3-сумісне сховище: Backblaze B2, Cloudflare R2, Hetzner Object Storage, MinIO. Кожен новий архів після створення сайт завантажує туди, і там зберігаються 8 найновіших.
-
У сховищі створіть бакет (приватний) — наприклад,
school-12-backups. -
Створіть ключ доступу, який може читати, писати й видаляти об’єкти лише в цьому бакеті. Запишіть обидві частини: ідентифікатор ключа і секрет.
-
Впишіть їх у
.envна сервері (nano /opt/tabula/app/.env):Змінна Що вписати BACKUP_S3_ENDPOINTАдреса API сховища (див. приклади нижче) BACKUP_S3_REGIONРегіон, якщо сховище його вимагає; порожнє — auto(так хоче R2)BACKUP_S3_BUCKETНазва бакета. Порожня — копії поза сервером немає BACKUP_S3_KEYІдентифікатор ключа доступу BACKUP_S3_SECRETСекрет ключа доступу BACKUP_S3_PREFIX«Папка» в бакеті, наприклад school-12/; порожня — корінь бакетаПриклади адрес:
- Backblaze B2:
BACKUP_S3_ENDPOINT=https://s3.eu-central-003.backblazeb2.com,BACKUP_S3_REGION=eu-central-003(регіон — частина адреси; ваша адреса показана в панелі B2 біля бакета); - Cloudflare R2:
BACKUP_S3_ENDPOINT=https://<account-id>.r2.cloudflarestorage.com,BACKUP_S3_REGION=(порожньо); - Hetzner Object Storage:
BACKUP_S3_ENDPOINT=https://fsn1.your-objectstorage.com.
- Backblaze B2:
-
Перезапустіть сайт, щоб він прочитав нові налаштування:
Terminal window cd /opt/tabula/appdocker compose -f docker-compose.prod.yml up -d --force-recreate app -
В адмін-панелі відкрийте «Бекапи» → «Зробити бекап». У блоці «Копія поза сервером» має бути «налаштовано» і «Остання вдала копія: …» з сьогоднішньою датою. Якщо змін не було і новий архів не створився, сайт однаково завантажить у сховище найновіший наявний архів, якщо його там ще немає. Помилку завантаження видно там само червоним рядком.
Ключі сховища вписують лише в .env на сервері, ніколи — в адмін-панель: з ними можна
видалити всі копії сайту. Невдале завантаження не ламає локальний бекап і повторюється на
наступному запуску.
Відновлення з архіву на сервері
Section titled “Відновлення з архіву на сервері”Відновлення з адмін-панелі навмисно недоступне — сайт не може безпечно підмінити свою ж базу даних, поки працює. Тому сайт спершу зупиняють, а скрипт відновлення відмовляється працювати, якщо бачить, що сайт запущений.
ssh deploy@<IP>cd /opt/tabula/appls /opt/tabula/backups # оберіть потрібний архівdocker compose -f docker-compose.prod.yml stop appdocker compose -f docker-compose.prod.yml run --rm \ -e DATA_DIR=/data \ -v /opt/tabula/data:/data \ -v /opt/tabula/backups:/backups \ app node scripts/restore.js /backups/<назва-архіву>.tar.gzdocker compose -f docker-compose.prod.yml up -d appПоточний стан сайту перед відновленням не знищується: скрипт копіює його в
/opt/tabula/backups/data.bak-<дата-час>. Якщо обрали не той архів, його можна повернути так
само.
Після відновлення всі користувачі входять в адмін-панель заново.
Архів з іншої інсталяції (наприклад, сайт, наповнений на іншому комп’ютері) відновлюють із
прапорцем --keep-users у кінці команди: облікові записи поточної бази переносяться у відновлену
(за поштою). Без прапорця в базі лишаються облікові записи з архіву.
Відновлення з копії в сховищі
Section titled “Відновлення з копії в сховищі”Якщо сервера більше немає (разом з /opt/tabula/backups): на новому сервері пройдіть кроки
1–6 встановлення з тими самими BACKUP_S3_* у .env, а в команді відновлення
замість шляху до файлу вкажіть s3://<назва-архіву>:
docker compose -f docker-compose.prod.yml stop appdocker compose -f docker-compose.prod.yml run --rm \ -e DATA_DIR=/data \ -v /opt/tabula/data:/data \ -v /opt/tabula/backups:/backups \ app node scripts/restore.js s3://school-12-backup-20260926-000000.tar.gzdocker compose -f docker-compose.prod.yml up -d appНазва архіву — без BACKUP_S3_PREFIX, така, як у списку бакета після префікса. Майстер першого
налаштування проходити не треба: у відновленій базі вже є облікові записи.
Навчальне відновлення — раз на пів року
Section titled “Навчальне відновлення — раз на пів року”Копія, з якої жодного разу не відновлювали, може виявитися непридатною саме тоді, коли знадобиться. Раз на пів року перевірте її на будь-якому комп’ютері з Node.js 20 і терміналом Linux чи macOS (не на робочому сервері):
git clone https://github.com/tabula-cms/tabula.gitcd tabulanpm ciexport BACKUP_S3_ENDPOINT=… BACKUP_S3_REGION=… BACKUP_S3_BUCKET=…export BACKUP_S3_KEY=… BACKUP_S3_SECRET=… BACKUP_S3_PREFIX=…DATA_DIR=./restore-drill npm run restore -- s3://<найновіший-архів>DATA_DIR=./restore-drill SESSION_SECRET=<будь-які 32+ символи> npm startСкрипти бекапу й відновлення читають змінні оточення, а не .env, тому BACKUP_S3_* задаються
через export. Відкрийте http://localhost:3000 і переконайтеся, що сторінки, новини й фото на
місці. Потім зупиніть сайт (Ctrl+C) і видаліть теку restore-drill.
Після перевірки запишіть її дату в адмін-панелі: «Бекапи» → блок «Тестове відновлення» → «Записати». Сторінка показує «Останнє тестове відновлення: …» — і видно, коли пора повторити.
Простіша щомісячна перевірка — кнопка «Перевірити» біля архіву на сторінці «Бекапи» в адмін-панелі: вона розпаковує архів, перевіряє цілісність бази й звіряє кількість записів і файлів з описом архіву.