Пошаговая инструкция · Самостоятельная установка
Как установить Novu на сервер и проверить доставку уведомлений
Пошаговая инструкция для самостоятельной установки. Развернём Novu Community, подключим почтового провайдера и вызовем уведомление через API. Разделим адреса панели, API и вебсокетов, закроем регистрацию и сохраним ключи вместе с базой.
Что подготовить и как читать инструкцию #
Novu принимает события вашего приложения и выполняет workflow: например, отправляет письмо или создаёт уведомление внутри интерфейса. Community поддерживает визуальные и программные workflow. Облачные SSO, MFA, RBAC, произвольные окружения и расширенное отслеживание доставки не следует считать частью этой установки. В актуальной матрице Community указан один участник команды, поэтому сначала оцените, подходит ли такое администрирование проекту.
Подготовьте Ubuntu 26.04 LTS, Docker Engine, Compose v2 и Nginx. Для всех компонентов на одной машине предусмотрите минимум около 4 vCPU и 12 ГБ памяти: официальная рекомендация выделяет 8 ГБ сервисам Novu и отдельно память MongoDB и Redis. Это ориентир документации, а не выполненное нами испытание; запас на SSD нужен для базы и очередей.
Команды выполняйте в терминале сервера после подключения по SSH, если шаг не указывает другое место. sudo даёт команде права администратора. Действия в браузере выполняйте на своём компьютере. Домены, IP-адреса и значения ВАШ_ПАРОЛЬ из примеров заменяйте своими до выполнения команды.
Двигайтесь по одному шагу и проверяйте результат перед следующим. Блок с <<EOF или другим именем завершения копируйте целиком: он записывает содержимое одного файла. При работе в nano сохраняйте файл клавишами Ctrl+O, Enter и выходите Ctrl+X. Если прервали SSH-сеанс, снова перейдите в указанный каталог; переменные предыдущего сеанса потребуется задать заново.
Настройка внешнего доступа использует обратный прокси Nginx и Certbot для Nginx. Приложение остаётся доступным на локальном порту; снаружи открыт его HTTPS-домен.
Шаг 1. Создайте каталог Novu #
Компоненты уведомлений будут описаны в ~/apps/novu. Перейдите в этот каталог перед следующими командами.
mkdir -p ~/apps/novu
cd ~/apps/novu
umask 077Текущий каталог — ~/apps/novu.
Шаг 2. Скачайте файлы выбранного релиза #
Загрузим официальный Compose и пример переменных из тега v3.19.0. Используем этот комплект, а не меняющуюся ветку latest.
curl -fL https://raw.githubusercontent.com/novuhq/novu/v3.19.0/docker/community/docker-compose.yml -o compose.yaml
curl -fL https://raw.githubusercontent.com/novuhq/novu/v3.19.0/docker/community/.env.example -o .envОба файла скачаны без ошибок HTTP.
Шаг 3. Заполните ключи и адреса новой установки #
Скрипт ниже меняет только перечисленные параметры .env: создаёт секреты и задаёт три внешних адреса. До копирования замените novu.example.com, novu-api.example.com и novu-ws.example.com своими доменами. STORE_ENCRYPTION_KEY защищает сохранённые реквизиты провайдеров. Не запускайте генератор повторно поверх рабочей установки.
python3 - <<'PATCH'
from pathlib import Path
import secrets
p = Path('.env')
updates = {
'JWT_SECRET': secrets.token_hex(32),
'STORE_ENCRYPTION_KEY': secrets.token_hex(16),
'NOVU_SECRET_KEY': secrets.token_hex(32),
'MONGO_INITDB_ROOT_PASSWORD': secrets.token_hex(32),
'NODE_ENV': 'production',
'HOST_NAME': 'https://novu.example.com',
'API_ROOT_URL': 'https://novu-api.example.com',
'VITE_API_HOSTNAME': 'https://novu-api.example.com',
'VITE_WEBSOCKET_HOSTNAME': 'https://novu-ws.example.com',
'FRONT_BASE_URL': 'https://novu.example.com',
'S3_LOCAL_STACK': '',
'DISABLE_USER_REGISTRATION': 'false',
}
lines = [line for line in p.read_text().splitlines() if line.split('=', 1)[0] not in updates]
lines.extend(key + '=' + value for key, value in updates.items())
p.write_text('\n'.join(lines) + '\n')
PATCHВ .env нет прежних пустых секретов; все три домена ваши.
Шаг 4. Проверьте настройки и внешнее хранилище #
Откройте .env. По исходной конфигурации задайте реквизиты реального S3-хранилища для вложений; LocalStack не используем как рабочее хранилище. Не заменяйте уже созданные ключи и пароль MongoDB.
chmod 600 .env
nano .envПоля адресов, ключей и S3 заполнены; файл защищён правами 600.
Шаг 5. Закройте внутренние порты и закрепите версии #
Скрипт изменяет только compose.yaml: образы Novu приводятся к 3.19.0, Redis получает постоянный том, HTTP/WS-порты привязываются к localhost. Не меняем домены, заданные в .env.
python3 - <<'PATCH'
from pathlib import Path
p = Path('compose.yaml')
s = p.read_text().replace(':3.18.0"', ':3.19.0"')
s = s.replace('image: "redis:alpine"', 'image: "redis:7-alpine"\n command: [redis-server, --appendonly, "yes", --appendfsync, everysec]\n volumes:\n - redis_data:/data')
s = s.replace('- ${API_PORT}:${API_PORT}', '- "127.0.0.1:${API_PORT}:${API_PORT}"')
s = s.replace('- ${WS_PORT}:${WS_PORT}', '- "127.0.0.1:${WS_PORT}:${WS_PORT}"')
s = s.replace('- 4000:4000', '- "127.0.0.1:4000:4000"')
s = s.replace(' IS_SELF_HOSTED: ${IS_SELF_HOSTED}', ' IS_SELF_HOSTED: ${IS_SELF_HOSTED}\n DISABLE_USER_REGISTRATION: ${DISABLE_USER_REGISTRATION}')
s = s.replace('volumes:\n mongodb:', 'volumes:\n redis_data:\n mongodb:')
p.write_text(s)
PATCHПри просмотре compose.yaml внешний интерфейс не публикует порты 3000, 3002 и 4000 напрямую.
Шаг 6. Проверьте конфигурацию #
Compose проверит файл и параметры. Ошибка отсутствующей переменной означает, что нужно вернуться к .env.
docker compose config --quietconfig --quiet завершился без ошибки.
Шаг 7. Скачайте образы #
Загрузим компоненты API, панели, WebSocket и исполнителя. Они должны быть одной согласованной версии.
docker compose pullpull завершился успешно.
Шаг 8. Запустите сервисы #
Первый запуск подготавливает MongoDB и приложение; дождитесь окончания команды.
docker compose up -dКонтейнеры созданы, затем проверим их готовность.
Шаг 9. Сохраните сайт Nginx #
Установите Nginx и Certbot по подготовке Ubuntu, если ещё не установили. Откройте sudo nano /etc/nginx/sites-available/novu и вставьте файл ниже. Замените все домены своими. Замените 203.0.113.10 внешним IP своего рабочего компьютера: ограничение действует только на панель, а проверка сертификата остаётся доступной. Nginx передаёт приложению домен, адрес клиента и протокол; Upgrade и Connection сохраняют WebSocket. До получения сертификата условие $scheme блокирует обычные HTTP-запросы кодом 403, чтобы вход не происходил без шифрования.
/etc/nginx/sites-available/novumap $http_upgrade $novu_connection_upgrade {
default upgrade;
"" close;
}
server {
listen 80;
server_name novu.example.com;
client_max_body_size 100m;
location / {
if ($scheme = http) { return 403; }
allow 203.0.113.10;
deny all;
proxy_pass http://127.0.0.1:4000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $novu_connection_upgrade;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
}
}
server {
listen 80;
server_name novu-api.example.com;
client_max_body_size 100m;
location / {
if ($scheme = http) { return 403; }
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $novu_connection_upgrade;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
}
}
server {
listen 80;
server_name novu-ws.example.com;
client_max_body_size 100m;
location / {
if ($scheme = http) { return 403; }
proxy_pass http://127.0.0.1:3002;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $novu_connection_upgrade;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
}
}
Сохраните Ctrl+O, Enter, Ctrl+X. В файле пока нет путей к сертификатам: Nginx сможет проверить его до выпуска HTTPS.
Шаг 10. Подключите сайт и проверьте HTTP #
Ссылка подключает сохранённый сайт. nginx -t проверяет все настройки сервера; && разрешает перечитать их только при успешной проверке. curl проверяет ответ для вашего домена. При ошибке остановитесь, исправьте указанную строку и повторите проверку.
sudo ln -sfn /etc/nginx/sites-available/novu /etc/nginx/sites-enabled/novu
sudo nginx -t && sudo systemctl reload nginx
curl -I http://novu.example.com/Ожидается successful от nginx -t и HTTP 403 для обычного запроса: до HTTPS это намеренная блокировка. DNS должен указывать на сервер, TCP 80/443 — быть доступен у провайдера. Ничего не вводите в форму входа по HTTP.
Шаг 11. Получите сертификат и включите HTTPS #
Каждое имя из команды должно указывать на этот сервер. Certbot проверит домены, сам добавит сертификат в сайт Nginx и включит перенаправление HTTP → HTTPS. Укажите свою почту при запросе и подтвердите условия Let’s Encrypt. Это отдельное действие после успешной проверки HTTP.
sudo /snap/bin/certbot --nginx -d novu.example.com -d novu-api.example.com -d novu-ws.example.com --redirectCertbot сообщил об успешной установке. Откройте HTTPS-адрес со своего компьютера: браузер не должен предупреждать о сертификате. Для закрытой панели используйте разрешённый IP. При ошибке исправьте DNS, в том числе AAAA, либо доступ к TCP 80; не повторяйте запросы выпуска подряд.
Шаг 12. Проверьте продление сертификата #
Certbot из snap создаёт службу автоматического продления. Dry-run выполняет пробное продление: настоящие сертификаты не заменяются. Затем проверьте, что таймер планирует следующий запуск. Сохраняйте доступ к TCP 80 для следующих проверок домена.
sudo /snap/bin/certbot renew --dry-run
systemctl list-timers --all 'snap.certbot.renew*'Пробное продление успешно, у таймера есть следующий запуск. Если тест не прошёл, проверьте /var/log/letsencrypt/letsencrypt.log; сначала исправьте причину. После этого можно создавать учётную запись и приглашать пользователей.
Шаг 13. Проверьте API и обработчик #
Состояние контейнеров и health-check показывают готовность; журнал worker важен для фактической отправки уведомлений.
docker compose ps
curl -f http://127.0.0.1:3000/v1/health-check
docker compose logs --tail=80 api worker wsAPI отвечает, worker/ws не перезапускаются.
Шаг 14. Создайте владельца организации #
В браузере откройте панель, зарегистрируйте первую учётную запись и организацию. Сохраните выбранные реквизиты. Подготовка остальных пользователей будет выполняться через согласованный доступ.
Владелец входит и видит настройки организации.
Шаг 15. Закройте свободную регистрацию #
В .env измените DISABLE_USER_REGISTRATION на true, сохранив ключи. Пересоздайте API, чтобы он прочитал новое значение.
# В .env установите DISABLE_USER_REGISTRATION=true.
nano .env
docker compose up -d --force-recreate apiПосторонняя регистрация отклоняется; существующий владелец входит.
Шаг 16. Подготовьте тестовое уведомление #
Подключите SMTP/email-провайдера в выбранном окружении, создайте workflow с идентификатором из примера ниже и subscriber с собственным email. Секрет API относится к этому же окружению. Если в запросе другой идентификатор, замените его реальным.
Workflow опубликован в тестовом окружении; адрес получателя ваш.
Шаг 17. Отправьте событие из терминала #
Введите API-ключ по скрытому запросу. curl отправит событие workflow, а unset уберёт ключ из текущего сеанса. Не записывайте ключ в публичный код браузера.
read -rsp 'API-ключ окружения Novu: ' NOVU_API_KEY
printf '\n'
curl -f https://novu-api.example.com/v1/events/trigger \
-H "Authorization: ApiKey $NOVU_API_KEY" \
-H 'Content-Type: application/json' \
--data '{"name":"server-check","to":{"subscriberId":"server-check-owner","email":"owner@example.com"},"payload":{}}'
unset NOVU_API_KEYAPI принял событие; в Activity видна обработка и на тестовый ящик пришло письмо.
Шаг 18. Остановите изменения перед копией #
Создадим закрытый каталог и остановим API/worker/ws. MongoDB останется доступной для выгрузки.
umask 077
backup_dir="$HOME/backups/novu/$(date -u +%Y%m%dT%H%M%SZ)"
mkdir -p "$backup_dir"
docker compose stop api worker wsКомпоненты приложения остановлены; переменная backup_dir задана в этом SSH-сеансе.
Шаг 19. Скопируйте MongoDB #
В базе находятся подписчики, workflow, настройки и история. mongodump читает реквизиты из контейнера.
docker compose exec -T mongodb sh -c 'exec mongodump --username "$MONGO_INITDB_ROOT_USERNAME" --password "$MONGO_INITDB_ROOT_PASSWORD" --authenticationDatabase admin --db novu-db --archive --gzip' > "$backup_dir/database.archive.gz"Архив базы создан без ошибки.
Шаг 20. Сохраните ключи и описание установки #
Скопируйте .env и Compose вместе с базой. Объекты S3 копируются средствами отдельного хранилища; один mongodump их не сохраняет.
cp .env compose.yaml "$backup_dir/"Ключи, база и копия S3 доступны вне установленного сервера.
Шаг 21. Запустите Novu и проверьте копию #
Верните API/worker/ws и повторите тестовое уведомление. Проверку восстановления проводите в отдельном окружении с тестовым провайдером, чтобы не отправлять рабочие уведомления.
docker compose start api worker wsТестовое письмо снова доставляется; копия возвращает workflow и подписчика.
Если результат шага отличается #
Вернитесь к проверке того шага, на котором появилась ошибка. Первая ошибка в выводе обычно полезнее последних строк: последующие команды могли не получить нужный файл или работающую службу. Не повторяйте установку целиком поверх частично настроенного приложения.
Если страница не открывается, сначала проверьте домен и входящие порты, затем состояние приложения и его журнал. Если сайт открывается, но отдельное действие не работает, проверьте связанные компоненты — обработчик фоновых задач, почту или права пользователя. Официальные источники в конце статьи относятся к выбранному способу установки.
Настройки внешнего доступа храните вместе с копией приложения: /etc/nginx/sites-available/novu и /etc/letsencrypt содержат сайт и сертификаты с закрытыми ключами. Архивируйте их с sudo в закрытое хранилище. После восстановления подключите сайт через sites-enabled, выполните sudo nginx -t, затем перечитайте Nginx. Для ошибки 502 сначала проверьте локальное приложение и /var/log/nginx/error.log; для сертификата — /var/log/letsencrypt/letsencrypt.log и таймер продления.