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

Встановлення

У командах нижче замініть:

  • school.example.ua — на ваш домен;
  • <IP> — на IP-адресу сервера;
  • 1.1.0 — на номер найновішої версії (список версій — на сторінці github.com/tabula-cms/tabula, розділ Releases).

Код і образи сайту беруться з публічного репозиторію проєкту tabula-cms/tabula — ця назва в командах лишається як є.

Каталог сайту на сервері — /opt/tabula (так він називається за замовчуванням). Якщо хочете інший, замінюйте його всюди однаково.

Крок 1. Підготувати сервер (один раз, від root)

Section titled “Крок 1. Підготувати сервер (один раз, від root)”

Під’єднайтеся до сервера як root — паролем, який видав хостинг, або через веб-консоль у панелі хостингу:

Terminal window
ssh root@<IP>

Завантажте й запустіть скрипт підготовки. У DEPLOY_PUBLIC_KEY вставте публічний ключ з попереднього розділу (рядок ssh-ed25519 AAAA…), у лапках:

Terminal window
curl -fsSL https://raw.githubusercontent.com/tabula-cms/tabula/main/scripts/server-setup.sh -o server-setup.sh
APP_DIR=/opt/tabula DEPLOY_PUBLIC_KEY="ssh-ed25519 AAAA..." bash server-setup.sh

Скрипт (scripts/server-setup.sh):

  • оновлює систему і встановлює Docker (з офіційного репозиторію Docker);
  • вмикає автоматичні оновлення безпеки системи й додає 2 ГБ swap (запас пам’яті на пікові моменти, наприклад оновлення сайту під час бекапу);
  • обмежує розмір журналів Docker, щоб вони не заповнили диск;
  • створює користувача deploy — ним ви надалі працюватимете з сайтом; він може керувати контейнерами, але не має прав адміністратора системи;
  • дозволяє вхід для deploy лише вашим ключем;
  • вмикає брандмауер: відкриті тільки SSH, HTTP (80) і HTTPS (443);
  • вимикає вхід по SSH паролем і прямий вхід root по SSH;
  • створює каталоги /opt/tabula/app (код), data (база й файли), backups (архіви), certbot (сертифікати); data і backups одразу належать користувачу, від якого працює сайт у контейнері, — окремо дозволяти йому запис не потрібно;
  • ставить часовий пояс Europe/Kyiv.

Скрипт можна запустити повторно — те, що вже зроблено, він пропускає.

⚠️ Після цього кроку пароль root по SSH більше не працює. Запасний вхід — веб-консоль у панелі хостингу (там пароль root і далі чинний).

З вашого комп’ютера під’єднайтеся як deploy:

Terminal window
ssh deploy@<IP>

Отримайте файли сайту — вони потрібні для запуску (docker-compose.prod.yml, шаблон nginx, скрипти). Беріть ту саму версію, що й образ, який запускатимете:

Terminal window
git clone https://github.com/tabula-cms/tabula.git /opt/tabula/app
cd /opt/tabula/app
git checkout v1.1.0

Усі подальші команди виконуються з каталогу /opt/tabula/app.

Крок 3. Файл налаштувань .env

Section titled “Крок 3. Файл налаштувань .env”

Створіть .env із зразка і відкрийте в редакторі:

Terminal window
cp .env.example .env
nano .env

(У nano: зберегти — Ctrl+O, Enter; вийти — Ctrl+X.)

Кожна змінна пояснена в самому файлі. Ось що в кожній написати для сервера:

Змінна Що вписати
PORT Залиште 3000. nginx і перевірка стану звертаються до застосунку саме на цей порт
LOG_LEVEL Залиште info. debug — лише на час пошуку проблеми
SITE_SLUG Коротке латинське ім’я сайту: малі літери, цифри, дефіс, до 32 символів, наприклад school-12. З нього складаються назва куки входу в адмін-панель і назви архівів бекапу. Потім не змінюйте: нове ім’я розлогінить усіх, а старі архіви зникнуть зі сторінки «Бекапи»
SITE_DOMAIN Домен без www і без https://: school.example.ua
SITE_URL Адреса сайту зі схемою, без скісної риски в кінці: https://school.example.ua. З неї будуються карта сайту, RSS і посилання для соцмереж
APP_DIR /opt/tabula
APP_IMAGE Образ сайту з номером версії, малими літерами: ghcr.io/tabula-cms/tabula:1.1.0
UPDATE_REPO tabula-cms/tabula — звідки сайт раз на добу дізнається про нові версії
UPDATE_CHECK 1 — перевіряти нові версії, 0 — не перевіряти
TRUST_PROXY 1. Сайт стоїть за nginx; з 0 вхід в адмін-панель не працюватиме
Змінна Що вписати
DATA_DIR, BACKUP_DIR Залиште порожніми: docker-compose.prod.yml задає їх сам (/opt/tabula/data і /opt/tabula/backups на сервері)
BACKUP_S3_ENDPOINT, BACKUP_S3_REGION, BACKUP_S3_BUCKET, BACKUP_S3_KEY, BACKUP_S3_SECRET, BACKUP_S3_PREFIX Копія бекапів поза сервером. Можна лишити порожніми зараз і заповнити пізніше — див. Бекапи і відновлення

Пошта форми зворотного зв’язку

Section titled “Пошта форми зворотного зв’язку”

Заповніть один із двох блоків. Без жодного сайт не запуститься; якщо заповнено обидва, діє SMTP.

Gmail:

Змінна Що вписати
EMAIL_USER Адреса Gmail, з якої надсилаються листи
EMAIL_PASS Пароль застосунку (App Password) цього акаунта, не звичайний пароль

Будь-який SMTP-сервер:

Змінна Що вписати
SMTP_HOST Адреса SMTP-сервера від постачальника пошти
SMTP_PORT Зазвичай 587; для 465 ще й SMTP_SECURE=1
SMTP_SECURE 0 для порту 587, 1 для порту 465
SMTP_USER, SMTP_PASS Логін і пароль до SMTP-сервера
MAIL_FROM Адреса відправника, наприклад no-reply@school.example.ua; порожня — SMTP_USER

І для обох варіантів:

Змінна Що вписати
CONTACT_TO Куди приходять листи з форми, наприклад скринька секретаря. Порожнє — на адресу відправника

Безпека і перше налаштування

Section titled “Безпека і перше налаштування”
Змінна Що вписати
SESSION_SECRET Довгий випадковий рядок, мінімум 32 символи. Згенеруйте: openssl rand -hex 32. Без нього сайт не запуститься; зміна потім виведе всіх з адмін-панелі
SETUP_CODE Одноразовий код для майстра першого налаштування. Найпростіше — задати свій: openssl rand -hex 6. Порожнє — сайт згенерує код сам і напише його в журнал запуску (див. крок 6)
HEALTH_TOKEN Необов’язково: довгий випадковий рядок (openssl rand -hex 32). Без нього /health ззовні показує лише status і version; із заголовком Authorization: Bearer <токен> — ще й стан бекапів і диска (так перевіряють сайт деплой через GitHub Actions і монітор)

Приклад заповненого .env (усі значення вигадані):

Terminal window
PORT=3000
LOG_LEVEL=info
SITE_SLUG=school-12
SITE_DOMAIN=school.example.ua
SITE_URL=https://school.example.ua
APP_DIR=/opt/tabula
APP_IMAGE=ghcr.io/tabula-cms/tabula:1.1.0
UPDATE_REPO=tabula-cms/tabula
UPDATE_CHECK=1
DATA_DIR=
BACKUP_DIR=
BACKUP_S3_ENDPOINT=
BACKUP_S3_REGION=
BACKUP_S3_BUCKET=
BACKUP_S3_KEY=
BACKUP_S3_SECRET=
BACKUP_S3_PREFIX=
TRUST_PROXY=1
SMTP_HOST=
SMTP_PORT=587
SMTP_SECURE=0
SMTP_USER=
SMTP_PASS=
MAIL_FROM=
EMAIL_USER=school12.site@gmail.com
EMAIL_PASS=abcd efgh ijkl mnop
CONTACT_TO=secretary@school.example.ua
SESSION_SECRET=<64 символи з openssl rand -hex 32>
SETUP_CODE=9b2e4c7a10d3
HEALTH_TOKEN=<64 символи з openssl rand -hex 32>

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

Terminal window
chmod 600 .env

Крок 4. Завантажити образ сайту

Section titled “Крок 4. Завантажити образ сайту”

Сайт на сервері не збирається з коду, а завантажується готовим Docker-образом — тим, що вказано в APP_IMAGE. Образ публічний, тож входити в реєстр не потрібно:

Terminal window
docker compose -f docker-compose.prod.yml pull app

Крок 5. Запустити сайт і отримати сертифікат HTTPS

Section titled “Крок 5. Запустити сайт і отримати сертифікат HTTPS”

Переконайтеся, що домен уже вказує на сервер (див. Домен і сервер). Потім:

Terminal window
docker compose -f docker-compose.prod.yml up -d app
CERTBOT_EMAIL="admin@school.example.ua" bash scripts/init-letsencrypt.sh
docker compose -f docker-compose.prod.yml up -d
  • CERTBOT_EMAIL — ваша справжня адреса: на неї Let’s Encrypt надсилає нагадування, якщо сертифікат не продовжився.
  • scripts/init-letsencrypt.sh запускається один раз: тимчасово піднімає nginx лише на HTTP, отримує сертифікат і повертає справжній конфіг з HTTPS. Сертифікат для www. він запитує, лише якщо для www є DNS-запис.
  • Далі сертифікат продовжується сам: контейнер certbot перевіряє його приблизно раз на 12 годин.

Перевірте, що все запущено:

Terminal window
docker compose -f docker-compose.prod.yml ps
curl -s https://school.example.ua/health

У ps мають бути app, nginx, certbot; app — зі станом healthy (з’являється за хвилину після старту). /health відповідає JSON, у якому "status":"ok" і "version":"1.1.0".

Відкрийте https://school.example.ua у браузері — має відкритися порожній сайт.

Крок 6. Перше налаштування в браузері

Section titled “Крок 6. Перше налаштування в браузері”

Поки на сайті немає жодного облікового запису, його налаштовують майстром за адресою https://school.example.ua/admin/setup. Майстер відкривається одноразовим кодом доступу:

  • якщо ви задали SETUP_CODE у .env — це він;

  • якщо ні — сайт згенерував код сам. Знайдіть його в журналі:

    Terminal window
    docker compose -f docker-compose.prod.yml logs app | grep admin/setup

    Рядок виглядає так: Сайт ще не налаштовано: відкрийте /admin/setup і введіть код xxxx-xxxx-xxxx.

Далі два варіанти:

  • пройдіть майстер самі, створивши обліковий запис адміністратора закладу (п’ять кроків: адміністратор, заклад, структура сайту, кольори, готово);
  • або передайте адресу й код тому, хто керуватиме сайтом (секретарю, директору), — майстер докладно описано в посібнику адміністратора.

Після останнього кроку майстер зникає назавжди (/admin/setup відповідає «не знайдено»), а згенерований файл з кодом видаляється. SETUP_CODE у .env після цього ні на що не впливає — його можна стерти.

П’ять неправильних кодів з однієї адреси закривають майстер на 15 хвилин.

Крок 7. Після встановлення

Section titled “Крок 7. Після встановлення”
  1. Увійдіть в адмін-панель (https://school.example.ua/admin) і перевірте на дашборді рядок про бекап. Перший бекап сайт робить сам одразу після запуску.
  2. Підключіть копію бекапів поза сервером — Бекапи і відновлення. Без неї всі копії зникнуть разом із сервером.
  3. Надішліть тестовий лист через форму на сторінці «Зворотний зв’язок» і переконайтеся, що він прийшов.
  4. Збережіть у надійному місці: IP сервера, пароль root, приватний SSH-ключ, копію .env. Без них перенести сайт чи повернути доступ буде важко.

Усі — з каталогу /opt/tabula/app під користувачем deploy:

Terminal window
docker compose -f docker-compose.prod.yml ps # що запущено
docker compose -f docker-compose.prod.yml logs -f app # журнал сайту (вихід — Ctrl+C)
docker compose -f docker-compose.prod.yml restart app # перезапустити сайт
curl -s https://school.example.ua/health # чи сайт відповідає (status, version)

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