MAATRIX / Блог / ntfy заменяет пуш-сервисы, пока телефон не ушёл в глубокий сон

ntfy заменяет пуш-сервисы, пока телефон не ушёл в глубокий сон

MAATRIX

Вы поднимаете ntfy за пять минут, подписываете телефон на топик, шлёте тестовый curl — уведомление прилетает мгновенно. А через неделю выясняется, что критичный алерт о падении сайта вы увидели только утром. Проблема не в ntfy и не в сервере — она живёт на самом телефоне, в механизмах энергосбережения Android, и её нужно чинить отдельно от установки сервиса.

Что такое ntfy и почему он так быстро завоевал симпатии

ntfy (произносится «нотифай») — это open-source сервис push-уведомлений, который работает поверх обычного HTTP. Никакой регистрации, никакого API-ключа по умолчанию: вы отправляете POST-запрос на URL с именем топика — и подписчики этого топика получают уведомление. На сервере это один бинарник на Go или один контейнер, минимум зависимостей, конфиг из десятка строк.

Топик — это просто произвольная строка в пути, вроде https://ntfy.example.com/server-alerts. Не нужно заранее «создавать» топик в админке — он появляется в момент первой публикации и исчезает, когда на него никто не подписан и не публикует (если не включено постоянное хранение). Это радикально снижает порог входа по сравнению с классическими push-провайдерами, где сначала нужно завести приложение в консоли разработчика и получить ключи.

Минимальный пример отправки:

curl -d "Диск на backup-01 заполнен на 92%" ntfy.example.com/server-alerts

Клиент на Android или iOS подписывается на топик через приложение ntfy (есть в Google Play, F-Droid и App Store), и с этого момента любое сообщение в топик прилетает на телефон как обычный пуш. Можно добавить заголовок, уровень приоритета, теги (превращаются в эмодзи), кнопки действий, вложения — всё через HTTP-заголовки, без SDK и без строчки кода на стороне мобильного приложения.

Именно эта простота — причина, почему ntfy стал стандартным выбором для «последней мили» в связке мониторинг → уведомление на телефон: Uptime Kuma, Prometheus Alertmanager, Grafana, cron-скрипты — все умеют слать обычный HTTP POST, а значит все умеют слать в ntfy без дополнительных библиотек.

Разворачиваем сервер: минимальный self-hosted вариант

Самый быстрый путь — Docker Compose. Создаём файл docker-compose.yml:

services:
  ntfy:
    image: binwiederhier/ntfy
    command:
      - serve
    environment:
      - TZ=Europe/Moscow
    volumes:
      - ./ntfy-cache:/var/cache/ntfy
      - ./ntfy-etc:/etc/ntfy
    ports:
      - "127.0.0.1:8080:80"
    restart: unless-stopped

Порт намеренно проброшен только на 127.0.0.1 — наружу сервис отдаём через реверс-прокси с TLS, а не голым HTTP на публичном порту. Пример конфига /etc/ntfy/server.yml, который стоит создать заранее и смонтировать вместо переменных окружения, если нужна более тонкая настройка:

base-url: "https://ntfy.example.com"
listen-http: ":80"
cache-file: "/var/cache/ntfy/cache.db"
auth-file: "/etc/ntfy/auth.db"
auth-default-access: "deny-all"
behind-proxy: true

Параметр auth-default-access: deny-all — важная деталь: без него любой человек в интернете, знающий (или подобравший) имя вашего топика, может и читать, и публиковать в него. Топики — не приватные каналы по умолчанию, а просто адреса, доступные всем, если явно не закрыты правами доступа.

Дальше настраиваем реверс-прокси — процесс подробно разобран в статье про настройку Nginx как реверс-прокси. Ключевой нюанс для ntfy — не забыть заголовки для корректной работы WebSocket и долгоживущих соединений (ntfy использует long-polling и WebSocket для доставки в реальном времени):

location / {
    proxy_pass http://127.0.0.1:8080;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_read_timeout 3600s;
}

Без увеличенного proxy_read_timeout соединение будет обрываться прокси раньше, чем это сделает сам ntfy, и клиенты будут чаще переподключаться — добавляет лишнюю нагрузку и небольшие задержки доставки.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Развернуть ntfy на VPS

Настройка доступа: пароли, токены, ACL по топикам

По умолчанию (без auth-file) сервер полностью открыт — это нормально для локального теста, но не для продакшена. Включаем аутентификацию:

docker exec -it ntfy ntfy user add --role=admin admin
docker exec -it ntfy ntfy user add readonly-alerts
docker exec -it ntfy ntfy access readonly-alerts "server-alerts" read-only
docker exec -it ntfy ntfy access readonly-alerts "*" deny

Эта связка создаёт администратора и отдельного пользователя, у которого есть право только читать конкретный топик server-alerts, а всё остальное запрещено явным правилом deny. Для публикации из мониторинга удобнее не логин-пароль, а токен доступа:

docker exec -it ntfy ntfy token add readonly-alerts

Токен подставляется в заголовок Authorization: Bearer <токен> при публикации curl-ом или из Alertmanager/Uptime Kuma. Держите список пользователей и топиков в отдельном документе с самого начала — когда их становится десяток (алерты, домашняя автоматизация, личные напоминания), без структуры ACL быстро превращается в кашу.

Отдельный момент — кэш сообщений. По умолчанию ntfy хранит их 12 часов, чтобы клиент, который был офлайн, получил пропущенные уведомления при следующем подключении — это спасает от части (но не всех) проблем с недоставкой, разобранных ниже. Отключать кэш заголовком Cache: no имеет смысл только для одноразовых уведомлений, где устаревшая информация хуже отсутствия уведомления.

Где ntfy реально спотыкается: доставка на телефон, а не на сервере

Вот здесь начинается то, из-за чего многие разочаровываются в self-hosted push, даже не разобравшись, в чём дело. Сервер отправил сообщение, лог показывает 200 OK, а телефон молчит. Причина почти всегда не в ntfy, а в том, как Android (и в меньшей степени iOS) относится к фоновым приложениям.

Как работает доставка в норме. Официальные приложения ntfy на Android, собранные из Google Play, используют Firebase Cloud Messaging (FCM) — то же «плечо» доставки, что и у большинства коммерческих пуш-сервисов, только полезная нагрузка (текст уведомления) идёт через ваш сервер, а не через серверы третьей стороны. F-Droid сборка и режим «без Google-сервисов» держат постоянное соединение (foreground service с WebSocket) вместо FCM — и именно этот сценарий страдает от энергосбережения сильнее всего: систему куда легче заставить «убить» долгоживущий фоновый сокет, чем единичный push через системный FCM-канал.

Doze mode и App Standby. Начиная с Android 6 система переводит устройство в режим Doze после периода бездействия с выключенным экраном: сеть отключается пачками, фоновые процессы приостанавливаются. FCM-пуши формально проходят сквозь Doze через высокоприоритетный канал, но если приложение при этом попадает под ограничения App Standby Buckets, доставка может откладываться на минуты, а иногда дольше.

Агрессивные прошивки китайских вендоров. Проблема серьёзнее стандартного Doze. MIUI (Xiaomi), EMUI/Magic UI (Huawei/Honor), ColorOS (Oppo/Realme), FunTouch OS (Vivo), собственные режимы OneUI (Samsung) добавляют свои слои энергосбережения поверх системного: агрессивно выгружают из памяти приложения, не добавленные в «автозапуск» или «защищённые», убивают фоновые сервисы через десятки секунд после блокировки экрана — вне зависимости от того, что говорит стандартный Android API. Проект dontkillmyapp.com каталогизирует эти особенности по производителям — полезно свериться, если ваш телефон именно такой.

Итог для честности: ntfy как протокол и сервер не «не умеет» доставлять пуши — он умеет ровно то, что позволяет конкретная связка модели телефона, прошивки и настроек энергосбережения. На «чистом» Android (Pixel, стоковые прошивки) и iOS проблема почти незаметна. На MIUI/EMUI/ColorOS без ручной настройки доставка может теряться регулярно, и это выглядит как «ntfy глючит», хотя сервер тут ни при чём.

Как настроить телефон, чтобы не терять критичные алерты

Порядок действий, который снижает потери до приемлемого минимума (полностью гарантировать доставку в фоне на Android невозможно в принципе — это фундаментальное архитектурное ограничение ОС, а не недоработка конкретного приложения):

  1. Отключить оптимизацию батареи для приложения ntfy. Настройки → Приложения → ntfy → Батарея → «Без ограничений» (формулировка отличается по прошивкам) — это системная настройка Android, доступная на любом устройстве.
  2. Добавить в автозапуск / защищённые приложения, если у вас MIUI, ColorOS, EMUI или похожая оболочка. Пункт обычно называется «Автозапуск» или «Защита приложений» (свайп-закрытие приложения из диспетчера задач на некоторых прошивках приравнивается к принудительной остановке процесса — так закрывать ntfy нельзя).
  3. Закрепить приложение в списке недавних задач («замок» на карточке приложения) — на многих прошивках это отдельный сигнал системе не выгружать процесс.
  4. Включить Instant Delivery, если используете официальное приложение через Google Play — это задействует FCM high-priority канал, который система обрабатывает в приоритете перед обычными фоновыми процессами.
  5. Проверить системный режим экономии заряда (не только настройки приложения) — он может дополнительно ограничивать фоновую сеть для всех приложений сразу, независимо от индивидуальных исключений.
  6. Задать приоритет сообщения в самом ntfy — Priority: urgent или Priority: high в заголовке запроса.
curl -H "Priority: urgent" -H "Title: Сайт недоступен" \
     -d "backend-01 не отвечает на HTTP уже 3 минуты" \
     https://ntfy.example.com/server-alerts

Проверка на практике: отправьте тестовое сообщение, заблокируйте экран и не трогайте телефон 20-30 минут (за это время обычно активируется Doze или фильтры вендора), затем отправьте второе. Если первое пришло сразу, а второе — только после разблокировки экрана, проблема именно в фоновом энергосбережении.

ntfy как канал уведомлений для мониторинга

Практическая ценность ntfy раскрывается в связке с системами мониторинга — как «последняя миля» между алертом и человеком. В Uptime Kuma это встроенный тип уведомления: Settings → Notifications → добавляем ntfy, указываем URL сервера, топик и токен. Если уведомления всё равно не приходят — прежде чем винить ntfy, стоит свериться со статьёй про частые причины, по которым Uptime Kuma не шлёт уведомления: там разобраны более базовые вещи вроде привязки канала к монитору и статуса Retries, которые легко спутать с проблемой доставки на телефон.

Для Prometheus Alertmanager интеграция идёт через webhook_configs, потому что нативного ntfy-ресивера в Alertmanager нет:

receivers:
  - name: 'ntfy'
    webhook_configs:
      - url: 'https://ntfy.example.com/server-alerts?Priority=urgent'
        send_resolved: true

Обратите внимание: Alertmanager отправляет JSON-тело в формате своего webhook, а не простой текст, который ntfy умеет разбирать «из коробки» — оно придёт как есть в теле уведомления. Для читаемого текста придётся либо ставить прослойку (скрипт-транслятор, который принимает вебхук и переформатирует в понятный ntfy-запрос), либо смириться с сырым JSON. Это тот самый нюанс, который редко упоминают в быстрых гайдах по установке.

Отдельный сценарий — использовать ntfy как резервный канал, если основной у вас Telegram-бот: если Telegram Bot API временно недоступен, а критичный алерт улетел только туда, вы узнаете о проблеме с опозданием. Подробнее в статье про резервный канал уведомлений на случай, если Telegram лёг — ntfy там хорошо подходит именно потому, что доставка идёт по прямому HTTP без зависимости от инфраструктуры мессенджера, хотя и наследует собственные ограничения доставки на телефон, разобранные выше.

Стоит сразу закладывать резервирование самого канала мониторинга — если алерты формально настроены, но никто не видит, куда они уходят, это классическая ловушка, разобранная в статье «мониторинг настроен, а алерты уходят в никуда»: один успешный тестовый пуш не гарантирует, что цепочка доставки останется рабочей через месяц, особенно если на телефоне тем временем прилетело обновление прошивки и сбросило настройки энергосбережения.

Что ntfy не умеет и когда стоит смотреть в другую сторону

Честный список ограничений, чтобы не разочаровываться постфактум:

  • Нет встроенных сценариев эскалации. Если никто не отреагировал на алерт за N минут, ntfy сам не позвонит следующему дежурному и не отправит SMS — для этого нужны инструменты уровня PagerDuty/Opsgenie или самописная логика поверх вебхуков.
  • Приоритет — подсказка, а не гарантия. Priority: urgent увеличивает шансы на своевременную доставку, но не отменяет ограничения конкретной прошивки: если приложение выгружено из памяти агрессивным энергосбережением, приоритет роли не играет, пока приложение не проснётся само.
  • Нет двусторонней аутентификации на уровне сообщений — любой, кто знает URL и (если нужно) токен, может опубликовать в топик. Компенсируется правильной настройкой ACL, но по умолчанию модель доверия простая.
  • Хранение сообщений не заменяет журнал инцидентов. Кэш на 12 часов — буфер для офлайн-клиентов, а не система хранения истории алертов с поиском и фильтрацией.
  • На iOS свои ограничения — Apple Push Notification service работает иначе, чем Android FCM, и рассчитывать на такую же гибкость фоновой доставки, как на «чистом» Android, не стоит.

Если вам принципиально важна гарантированная доставка с эскалацией и SLA, ntfy на этом этапе не заменит полноценный incident management. Но как простой, бесплатный и полностью подконтрольный вам канал для алертов, домашней автоматизации и личных напоминаний — это один из самых практичных self-hosted инструментов, который можно поднять за вечер.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Развернуть ntfy на VPS

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Чем ntfy отличается от Gotify?

Оба решают похожую задачу — self-hosted push без зависимости от коммерческих облачных сервисов. ntfy работает через простой HTTP-протокол без обязательной регистрации приложения на сервере (топик создаётся публикацией). Но главное, что объединяет оба инструмента: и ntfy, и Gotify одинаково зависят от того, насколько агрессивно конкретная прошивка Android «убивает» фоновые процессы — разбор в этой статье (Doze, автозапуск, белые списки энергосбережения) актуален для обоих.

Можно ли использовать ntfy без публичного домена и TLS?

Технически да, локально в домашней сети через IP и HTTP. Но мобильные приложения и большинство браузеров ограничивают функциональность для не-HTTPS соединений, а без выхода в интернет вы не получите уведомления, когда телефон вне вашей Wi-Fi сети — то есть именно тогда, когда push и нужен.

Что произойдёт, если сервер ntfy будет недоступен несколько часов?

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

Нужен ли отдельный сервер под ntfy?

Сервис лёгкий — для личного использования хватает пары сотен мегабайт оперативной памяти, спокойно уживается на том же VPS, что и остальная инфраструктура. Отдельный сервер имеет смысл, только если ntfy — критичный алертинг-канал для нескольких независимых проектов и вы не хотите, чтобы авария на основном сервере убила заодно и канал уведомлений о ней самой.

Как проверить доставку, не дожидаясь реального инцидента?

Заведите cron-задачу, которая раз в час публикует тестовое сообщение с текущим временем в отдельный топик, и сверяйте на телефоне, совпадает ли время получения с временем отправки. Регулярные расхождения в несколько минут — сигнал, что энергосбережение на конкретном телефоне всё ещё режет доставку.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →