Удельта

Пошаговая инструкция · Самостоятельная установка

Как установить ntfy на Ubuntu: закрытые уведомления для команды

Пошаговая инструкция для самостоятельной установки. Установим закрытый ntfy, создадим разных пользователей для скрипта и телефона и проверим уведомление. Каждый небольшой шаг объясняет действие и показывает, какой результат ждать.

Первое push-уведомление проходит через собственный сервер к подписанному мобильному устройству

Что подготовить #

Установим ntfy 2.28.0 для уведомлений из скриптов. Сообщения попадают в тему — канал с именем, например backups. Скрипту разрешим отправку, а телефону — чтение. ntfy не требует SMTP для таких уведомлений.

Нужны Ubuntu 26.04 LTS, Docker Engine, Compose v2 и Nginx из подготовки сервера. Команды выполняйте в SSH-терминале сервера от пользователя с sudo. Для этой схемы используются SQLite и файлы: отдельная PostgreSQL не нужна. При ошибке остановитесь на текущем шаге.

Создайте A-запись своего домена на IP сервера. Во всех примерах замените notify.example.com своим доменом. Порты 80/443 доступны Nginx, 8085 наружу не открывайте. Для теста подготовьте приложение ntfy на телефоне; начать можно с браузера без приложения.

Настройка внешнего доступа использует обратный прокси Nginx и Certbot для Nginx. Приложение остаётся доступным на локальном порту; снаружи открыт его HTTPS-домен.

Шаг 1. Создайте постоянные каталоги #

config хранит настройки, state — пользователей и права, cache — сообщения и вложения. Следующие команды выполняйте из ~/services/ntfy.

Создать каталоги ntfy
mkdir -p ~/services/ntfy/{config,state,cache}
cd ~/services/ntfy
chmod 700 state config

Каталоги созданы. При новом входе по SSH сначала выполните cd ~/services/ntfy.

Шаг 2. Настройте закрытый сервер #

Откройте nano config/server.yml и вставьте файл ниже, заменив домен. deny-all запрещает доступ без выданных прав. cache-duration: 24h хранит сообщения сутки; телефон, отсутствующий дольше, не получит удалённую историю.

config/server.yml
base-url: https://notify.example.com
listen-http: ':80'
behind-proxy: true
cache-file: /var/cache/ntfy/cache.db
cache-duration: 24h
auth-file: /var/lib/ntfy/user.db
auth-default-access: deny-all
attachment-cache-dir: /var/cache/ntfy/attachments

Сохраните Ctrl+O, Enter, Ctrl+X. В файле указан ваш HTTPS-домен и политика deny-all; название темы само по себе не считается паролем.

Шаг 3. Сохраните Compose #

Откройте nano compose.yaml и вставьте файл ниже. Он связывает каталоги с контейнером; удаление контейнера не удаляет эти файлы.

compose.yaml
services:
  ntfy:
    image: binwiederhier/ntfy:v2.28.0
    command: serve
    restart: unless-stopped
    ports:
      - '127.0.0.1:8085:80'
    volumes:
      - ./config:/etc/ntfy:ro
      - ./state:/var/lib/ntfy
      - ./cache:/var/cache/ntfy

Сохраните Ctrl+O, Enter, Ctrl+X. Порт 8085 опубликован только на 127.0.0.1 — доступен серверу и Nginx.

Шаг 4. Проверьте Compose #

Перед запуском проверим структуру YAML.

Проверить файл запуска
docker compose config --quiet

Команда завершилась без вывода. При ошибке исправьте указанную строку и повторите проверку.

Шаг 5. Загрузите ntfy #

Docker скачает официальный образ выбранной версии.

Загрузить образ
docker compose pull

Скачивание завершилось без ошибки сети или неизвестного тега.

Шаг 6. Запустите сервер уведомлений #

Контейнер начнёт работать в фоне и будет перезапускаться после перезагрузки сервера.

Запустить ntfy
docker compose up -d

Сервис создан; подождите несколько секунд перед проверкой.

Шаг 7. Проверьте локальный ответ #

Этот адрес проверяет приложение без DNS и HTTPS.

Проверить здоровье ntfy
curl --fail http://127.0.0.1:8085/v1/health

Получен успешный ответ. При отказе соединения посмотрите docker compose logs --tail=80 ntfy и не переходите дальше.

Шаг 8. Сохраните сайт Nginx #

Установите Nginx и Certbot по подготовке Ubuntu, если ещё не установили. Откройте sudo nano /etc/nginx/sites-available/ntfy и вставьте файл ниже. Замените все домены своими. Nginx передаёт приложению домен, адрес клиента и протокол; Upgrade и Connection сохраняют WebSocket. До получения сертификата условие $scheme блокирует обычные HTTP-запросы кодом 403, чтобы вход не происходил без шифрования. Буферизация запросов и ответов отключена: подписчик получает уведомления сразу, а загрузки не копятся целиком на прокси.

Файл /etc/nginx/sites-available/ntfy
map $http_upgrade $ntfy_connection_upgrade {
    default upgrade;
    "" close;
}

server {
    listen 80;
    server_name notify.example.com;
    client_max_body_size 100m;
    location / {
        if ($scheme = http) { return 403; }
        proxy_pass http://127.0.0.1:8085;
        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 $ntfy_connection_upgrade;
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
        proxy_buffering off;
        proxy_request_buffering off;
    }
}

Сохраните Ctrl+O, Enter, Ctrl+X. В файле пока нет путей к сертификатам: Nginx сможет проверить его до выпуска HTTPS.

Шаг 9. Подключите сайт и проверьте HTTP #

Ссылка подключает сохранённый сайт. nginx -t проверяет все настройки сервера; && разрешает перечитать их только при успешной проверке. curl проверяет ответ для вашего домена. При ошибке остановитесь, исправьте указанную строку и повторите проверку.

Подключите сайт и проверьте HTTP
sudo ln -sfn /etc/nginx/sites-available/ntfy /etc/nginx/sites-enabled/ntfy
sudo nginx -t && sudo systemctl reload nginx
curl -I http://notify.example.com/

Ожидается successful от nginx -t и HTTP 403 для обычного запроса: до HTTPS это намеренная блокировка. DNS должен указывать на сервер, TCP 80/443 — быть доступен у провайдера. Ничего не вводите в форму входа по HTTP.

Шаг 10. Получите сертификат и включите HTTPS #

Каждое имя из команды должно указывать на этот сервер. Certbot проверит домены, сам добавит сертификат в сайт Nginx и включит перенаправление HTTP → HTTPS. Укажите свою почту при запросе и подтвердите условия Let’s Encrypt. Это отдельное действие после успешной проверки HTTP.

Получите сертификат и включите HTTPS
sudo /snap/bin/certbot --nginx -d notify.example.com --redirect

Certbot сообщил об успешной установке. Откройте HTTPS-адрес со своего компьютера: браузер не должен предупреждать о сертификате. Для закрытой панели используйте разрешённый IP. При ошибке исправьте DNS, в том числе AAAA, либо доступ к TCP 80; не повторяйте запросы выпуска подряд.

Шаг 11. Проверьте продление сертификата #

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; сначала исправьте причину. После этого можно создавать учётную запись и приглашать пользователей.

Шаг 12. Создайте администратора #

У ntfy нет обязательного мастера первого владельца в браузере. Создадим администратора командой: пароль вводится интерактивно и не отображается.

Создать пользователя owner
docker compose exec ntfy ntfy user add --role=admin owner

Команда подтвердила создание owner. Сохраните пароль в менеджере паролей; администратор может читать все темы.

Шаг 13. Создайте отдельного отправителя #

Скрипт не должен использовать пароль администратора. Создайте обычного пользователя backup-script; задайте другой длинный пароль по запросу.

Создать пользователя для скрипта
docker compose exec ntfy ntfy user add backup-script

Пользователь создан, но пока не имеет доступа к теме.

Шаг 14. Разрешите отправку в одну тему #

Право write-only позволяет отправлять сообщения в backups, сохраняя чтение закрытым.

Выдать право публикации
docker compose exec ntfy ntfy access backup-script backups write-only

Команда подтвердила правило для backups. Доступ к другим темам не выдан.

Шаг 15. Создайте читателя для телефона #

Заведите отдельного пользователя phone-reader и выдайте ему только чтение backups. Пароль снова вводится интерактивно.

Создать читателя одной темы
docker compose exec ntfy ntfy user add phone-reader
docker compose exec ntfy ntfy access phone-reader backups read-only

Пользователь создан и получил read-only. В приложении теперь можно обойтись без административной записи.

Шаг 16. Отправьте первое сообщение #

curl вызовет HTTP-адрес темы и запросит пароль backup-script. Замените домен вашим. Сообщение тестовое: секреты и документы в уведомления не отправляйте.

Опубликовать тестовое уведомление
curl --fail --user backup-script --data 'Пробная копия завершена' https://notify.example.com/backups

Сервер вернул описание сообщения без ошибки. Это подтверждает отправку; доставку на телефон проверим отдельно.

Шаг 17. Проверьте отказ без пароля #

Повторим чтение темы без авторизации. Эта проверка покажет, что права работают и случайный посетитель не видит историю.

Проверить закрытую тему
curl -i 'https://notify.example.com/backups/json?poll=1'

Ожидается HTTP 401 или 403, а не содержимое сообщения. Если возвращается история, остановитесь и проверьте auth-default-access и права.

Шаг 18. Подпишитесь с телефона #

В приложении ntfy добавьте собственный сервер, пользователя phone-reader и тему backups. Отправьте ещё одно сообщение командой шага 14. Затем сверните приложение и повторите тест при выключенном экране и на мобильной сети.

Сообщение видно в теме, а фоновая доставка работает в нужных условиях. На Android проверьте разрешения уведомлений и энергосбережение. Для быстрой доставки iOS официальный способ использует upstream-base-url на ntfy.sh; это отдельная зависимость. Перед её добавлением сверяйте раздел мобильной доставки в источниках.

Шаг 19. Сохраните данные согласованно #

Коротко остановим ntfy и скопируем пользователей, сообщения и конфигурацию. Сначала создайте место для копий; архив с секретами храните закрытым.

Создать копию с остановкой
umask 077
mkdir -p "$HOME/backups/ntfy"
docker compose stop ntfy
tar -czf "$HOME/backups/ntfy/ntfy-$(date -u +%Y%m%dT%H%M%SZ).tar.gz" config state cache compose.yaml

Архив создан без ошибки. Если tar сообщил ошибку, сохранение не завершено; выясните причину. ntfy пока остановлен.

Шаг 20. Верните уведомления #

Запустим ntfy после копирования, затем снова отправим сообщение обычным пользователем.

Возобновить ntfy
docker compose start ntfy

Публикация из шага 14 и чтение на телефоне работают. Перенесите архив в отдельное защищённое хранилище.

Если шаг не работает и как обновлять #

В ~/services/ntfy выполните docker compose ps и docker compose logs --tail=80 ntfy. При 401/403 проверьте пользователя и право на конкретную тему. При 502 повторите локальный /v1/health. Если отправка успешна, а телефон молчит, проверьте подписку, длительные соединения прокси, разрешения и фоновые ограничения.

Копию испытайте на отдельной машине с тем же v2.28.0, другим доменом и без рабочих отправителей: должны сохраниться пользователи, права и история. Перед обновлением прочитайте изменения, создайте копию и проверьте новый точный тег на восстановленной установке. После обновления повторите отправку, отказ постороннему и фоновую доставку. Не удаляйте SQLite-файлы ради повторного запуска.

Настройки внешнего доступа храните вместе с копией приложения: /etc/nginx/sites-available/ntfy и /etc/letsencrypt содержат сайт и сертификаты с закрытыми ключами. Архивируйте их с sudo в закрытое хранилище. После восстановления подключите сайт через sites-enabled, выполните sudo nginx -t, затем перечитайте Nginx. Для ошибки 502 сначала проверьте локальное приложение и /var/log/nginx/error.log; для сертификата — /var/log/letsencrypt/letsencrypt.log и таймер продления.

Обсудить установку и настройку

Материал был полезен?

Сообщить об ошибке или задать вопрос редакции

Сообщение увидит редакция. Не указывайте пароли и личные данные. Текст удаляется через 180 дней.

О материале

ИнфраструктураРуководствоЗадача: установитьУровень: практикаntfy

Последнее обновление:

Обсуждение

Комментарии проходят подтверждение адреса и проверку редакцией.