Перейти до вмісту

Бекапи і відновлення

Налаштовувати 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.

Щоб дізнатися, що сайт не відповідає чи сертифікат не продовжився, раніше, ніж про це скажуть відвідувачі, підключіть безкоштовний зовнішній монітор (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 найновіших.

  1. У сховищі створіть бакет (приватний) — наприклад, school-12-backups.

  2. Створіть ключ доступу, який може читати, писати й видаляти об’єкти лише в цьому бакеті. Запишіть обидві частини: ідентифікатор ключа і секрет.

  3. Впишіть їх у .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.
  4. Перезапустіть сайт, щоб він прочитав нові налаштування:

    Terminal window
    cd /opt/tabula/app
    docker compose -f docker-compose.prod.yml up -d --force-recreate app
  5. В адмін-панелі відкрийте «Бекапи» → «Зробити бекап». У блоці «Копія поза сервером» має бути «налаштовано» і «Остання вдала копія: …» з сьогоднішньою датою. Якщо змін не було і новий архів не створився, сайт однаково завантажить у сховище найновіший наявний архів, якщо його там ще немає. Помилку завантаження видно там само червоним рядком.

Ключі сховища вписують лише в .env на сервері, ніколи — в адмін-панель: з ними можна видалити всі копії сайту. Невдале завантаження не ламає локальний бекап і повторюється на наступному запуску.

Відновлення з архіву на сервері

Section titled “Відновлення з архіву на сервері”

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

Terminal window
ssh deploy@<IP>
cd /opt/tabula/app
ls /opt/tabula/backups # оберіть потрібний архів
docker compose -f docker-compose.prod.yml stop app
docker 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.gz
docker 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://<назва-архіву>:

Terminal window
docker compose -f docker-compose.prod.yml stop app
docker 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.gz
docker compose -f docker-compose.prod.yml up -d app

Назва архіву — без BACKUP_S3_PREFIX, така, як у списку бакета після префікса. Майстер першого налаштування проходити не треба: у відновленій базі вже є облікові записи.

Навчальне відновлення — раз на пів року

Section titled “Навчальне відновлення — раз на пів року”

Копія, з якої жодного разу не відновлювали, може виявитися непридатною саме тоді, коли знадобиться. Раз на пів року перевірте її на будь-якому комп’ютері з Node.js 20 і терміналом Linux чи macOS (не на робочому сервері):

Terminal window
git clone https://github.com/tabula-cms/tabula.git
cd tabula
npm ci
export 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.

Після перевірки запишіть її дату в адмін-панелі: «Бекапи» → блок «Тестове відновлення» → «Записати». Сторінка показує «Останнє тестове відновлення: …» — і видно, коли пора повторити.

Простіша щомісячна перевірка — кнопка «Перевірити» біля архіву на сторінці «Бекапи» в адмін-панелі: вона розпаковує архів, перевіряє цілісність бази й звіряє кількість записів і файлів з описом архіву.