Пять уровней самостоятельности: от публичного топика до сервера с ACL
Схема «алерты в телеграм-бота» стала настолько стандартной, что её перестали обсуждать. А обсудить есть что: токен бота размазан по переменным окружения половины ваших контейнеров, канал доставки целиком чужой, а в момент, когда бота забанят или мессенджер перестанет открываться, вы окажетесь единственным админом, который не в курсе, что у него горит сервер.
Альтернатива, требующая примерно нулевых усилий на входе, — ntfy: сервер на Go, который принимает обычный HTTP POST и превращает его в push на телефоне. Никакого SDK, никакого формата вебхука, который надо изучать: curl -d "текст" сервер/топик — и всё. При этом сервер можно поднять свой, и тогда цепочка доставки принадлежит вам целиком. Почти целиком — с iOS есть нюанс, к которому мы дойдём.
Дальше — маршрут по уровням: от «попробовать за тридцать секунд» до сервера с логинами, токенами и правами на топики. Каждый уровень самодостаточен: можно остановиться там, где вам хватит.
ℹ️ Справка
Актуальная версия на момент написания — v2.24.0 от 4 июня 2026. Проект живой, но релизы редкие: предыдущие — 2.23.0 (18 мая) и 2.22.0 (21 апреля 2026). Это нормальный ритм для инструмента, который делает одну вещь и уже её сделал.
Уровень 0. Чужой сервер и один curl
Публичный ntfy.sh не требует ни регистрации, ни ключей. Топик создаётся в момент первой отправки:
Подписываетесь на тот же топик в приложении или на https://ntfy.sh/moi-domashnii-server-x7k2 в браузере — и сообщение приходит.
⛔ Так делать нельзя
Ровно здесь совершают главную ошибку. В документации это сказано прямым текстом: топик — это по сути пароль. Кто угадал имя, тот читает все ваши уведомления и может слать свои. ntfy.sh/backup, ntfy.sh/home, ntfy.sh/alerts — это публичная лента, на которую подписан кто угодно.
Имя топика ограничено 64 символами, допустимы буквы, цифры, - и _. Если остаётесь на публичном сервере — генерируйте его как пароль:
head -c 24 /dev/urandom | base64 | tr -d '/+='
Для чего этого уровня хватает: разовые скрипты, «дай знать, когда доедет rsync», уведомления, содержимое которых не жалко показать посторонним. Для всего остального — дальше.
Уровень 1. Свой ntfy в Docker
Образ binwiederhier/ntfy собирается под amd64, armv6, armv7 и arm64 — то есть спокойно едет на Raspberry Pi и на любом мини-ПК.
Внутри контейнера сервер слушает 80-й порт (listen-http: ":80" по умолчанию), рядом есть listen-https (:443) и listen-unix — если вы предпочитаете отдавать сокет прокси-серверу напрямую, минуя TCP.
cache-file — это SQLite, в котором лежат сообщения. Без него сервер работает целиком в памяти: перезапустили контейнер — история пропала, а подписчик, который был офлайн, ничего не получит. Практически всегда его надо задать.
Уровень 2. За обратным прокси
Здесь живёт грабля, на которую наступают почти все.
⚠️ Осторожно
Если ntfy стоит за nginx, Caddy или Traefik и вы не выставили behind-proxy: true, сервер видит всех клиентов как один IP — адрес прокси. А лимиты в ntfy считаются по «посетителю», то есть по IP. Результат: один активный скрипт съедает лимит на всех, остальные получают 429.
behind-proxy: true
proxy-forwarded-header: "X-Forwarded-For" # значение по умолчанию
Второе, что обязательно на этом уровне, — корректный base-url. Он используется в ссылках на вложения и в письмах; если он не совпадает с реальным внешним адресом, вложения будут приходить со ссылками в никуда.
Третье — сам прокси. ntfy держит длинные соединения для подписчиков, поэтому буферизация ответа и короткий таймаут прокси ломают доставку. В nginx это proxy_buffering off; и увеличенный proxy_read_timeout, в Caddy обычно достаточно обычного reverse_proxy без дополнительных настроек.
Уровень 3. Кто кому что может
По умолчанию сервер открыт: любой, кто знает адрес, публикует и читает. Закрывается это одной строкой:
Дальше — CLI. Команды выполняются внутри контейнера (docker compose exec ntfy ntfy ...):
# администратор — имеет доступ ко всему
ntfy user add --role=admin admin
# обычный пользователь: по умолчанию не имеет прав ни на что
ntfy user add homeassistant
# выдаём права точечно
ntfy access homeassistant "alerts-*" write-only
ntfy access phone "alerts-*" read-only
# токен вместо пароля для скриптов
ntfy token add --expires=90d homeassistant
Шаблон alerts-* работает — права выдаются на группу топиков, а не только на один.
🔒 Безопасность
Токен лучше пароля не потому, что он длиннее, а потому что его можно отозвать по одному, не меняя доступ везде сразу, и можно выдать со сроком жизни. Для сервиса, который только шлёт, ставьте write-only: скомпрометированный контейнер тогда не сможет прочитать чужие уведомления.
В версии 2.24.0, кстати, чинили как раз проблему в этой области — некорректное сравнение имён топиков в ACL без учёта регистра на SQLite. Если вы держите ntfy с правами доступа, обновиться стоит.
Уровень 4. Телефон, и почему iOS требует чужого сервера
Это самая недооценённая часть. Одно и то же приложение доставляет уведомления тремя разными способами, и от способа зависит и задержка, и приватность, и расход батареи.
Схема на основе документации docs.ntfy.sh, разделы subscribe/phone и config
Android, сборка из Google Play. Firebase используется только для топиков на самом ntfy.sh. Для ваших собственных серверов приложение Firebase не задействует вообще — оно держит прямое соединение. Это прямая цитата из документации, и она снимает главное опасение: свой сервер не «ходит через Google».
Android, сборка из F-Droid. Firebase нет в принципе, все подписки по умолчанию идут в режиме мгновенной доставки.
Мгновенная доставка — это постоянное соединение, а значит foreground-сервис и вечное уведомление в шторке. Без него, предупреждает документация, сообщения могут приходить с задержкой в минуты и даже часы. Выбор здесь честный и явный: либо иконка в статус-баре, либо непредсказуемая задержка.
❗ Главное
iOS — отдельный случай. Apple не разрешает приложениям держать постоянное соединение, всё идёт через APNS. Достучаться до APNS может только владелец сертификата приложения, то есть проект ntfy. Поэтому для своего сервера в конфиг добавляют:
upstream-base-url: "https://ntfy.sh"
Ваш сервер при поступлении сообщения отправляет на ntfy.sh короткий poll-запрос, тот будит телефон через APNS, а телефон приходит к вашему серверу за телом сообщения. Наружу уходит факт события, а не его содержимое.
Если вас и это не устраивает — остаётся вариант «зайти в веб-интерфейс и подписаться там» через Web Push, для которого нужны ключи VAPID:
Ключи генерируются командой ntfy webpush keys. Заметьте, что Web Push в браузере тоже ходит через сервисы Google/Mozilla/Apple — полностью изолированного пути на смартфон сегодня просто не существует.
Что можно положить в уведомление
Это то, ради чего ntfy стоит предпочесть самописному вебхуку: сообщение — не просто строка.
Заголовок
Короткие псевдонимы
Что делает
X-Title
t, ti
заголовок уведомления
X-Priority
p, prio
приоритет 1…5
X-Tags
tag, ta
эмодзи и метки
X-Click
Click
URL, открываемый по тапу
X-Actions
Action
до 3 кнопок действия
X-Attach
a
вложение по внешней ссылке
X-Markdown
md
включить разметку
X-Delay
Delay
отложенная отправка
X-Email
Email
продублировать на почту
Приоритеты влияют на поведение телефона:
Уровень
Имя
Поведение
5
urgent / max
длинная вибрация, всплывающее уведомление
4
high
длинная вибрация, всплывающее
3
default
короткая вибрация, обычное
2
low
без звука и вибрации, скрыто до открытия шторки
1
min
без звука, «под сгибом»
Самое интересное — кнопки. Их три типа: view (открыть ссылку), http (сделать HTTP-запрос) и broadcast (отправить Android-интент в Tasker и подобное). Классический сценарий домашнего сервера:
Одно уведомление — и действие выполняется с экрана блокировки, без открытия Home Assistant.
✅ Как правильно
Полезные мелочи, которые редко находят сразу:
X-Delay принимает 30s, 5m, 2h или Unix-таймстамп — готовый планировщик напоминаний без cron.
Markdown: yes превращает тело в размеченный текст: список отвалившихся сервисов читается несравнимо лучше.
Тело до 4096 байт. Больше — сервер попробует превратить это во вложение.
JSON-публикация на корень сервера: curl ntfy.example.org -d '{"topic":"alerts-infra","title":"…","priority":5}' — удобно, когда отправляет не bash, а код.
Куда это подключается на практике
systemd. В юнит добавляется OnFailure=, который дёргает одноразовый сервис с curl. Падение любого сервиса — сразу в телефон.
cron и бэкап-скрипты.curl -d "..." -H "Priority: 5" в ветке ошибки — и вы наконец узнаёте о неудавшемся бэкапе не через месяц.
Uptime Kuma и Healthchecks. ntfy у обоих в списке нотификаторов из коробки.
Home Assistant. Через rest_command или интеграцию ntfy; сюда же — кнопки действий с примера выше.
Даже на своём сервере лимиты включены — они защищают от того, чтобы один взбесившийся скрипт не забил диск.
Параметр
Значение по умолчанию
Смысл
visitor-request-limit-burst
60
сколько запросов можно сделать залпом
visitor-request-limit-replenish
5s
как быстро «бак» пополняется
attachment-file-size-limit
15M
максимум на один файл
attachment-total-size-limit
5G
общий кэш вложений
attachment-expiry-duration
3h
сколько живёт вложение
visitor-attachment-total-size-limit
100M
квота на «посетителя»
visitor-attachment-daily-bandwidth-limit
500M
суточный трафик вложений
Три часа хранения вложений — это про то, что ntfy не файлопомойка. Скриншот с камеры дойдёт, архив логов — нет, и не должен.
⚠️ Осторожно
Ещё одна деталь для тех, кто выставляет сервер наружу: в 2.23.0 добавили лимит на создание новых топиков с одного адреса — именно чтобы нельзя было перебором нащупать существующие. Если у вас открытый сервер с auth-default-access: read-write, перебор имён топиков остаётся полностью рабочей атакой. Это ещё один аргумент за deny-all.
Чего ntfy не делает
Честности ради: это не мессенджер и не система оповещений с дежурными сменами.
Здесь нет шифрования сообщений между отправителем и получателем — трафик защищён TLS, но на сервере сообщение лежит открытым текстом в SQLite. Нет эскалаций «не подтвердил за 5 минут — звони следующему», нет расписаний дежурств, нет группировки и подавления шквала одинаковых алертов. Если вам нужно это — смотрите в сторону Alertmanager перед ntfy, а ntfy оставьте как транспорт последней мили.
И ещё: своим сервером вы берёте на себя его доступность. Уведомление о том, что сервер лёг, не придёт с сервера, который лёг. Классическое решение — держать ntfy не на той же машине, за которой он следит, а внешний watchdog вроде Healthchecks.io — на стороне.
Чем вы будите себя, когда падает домашний сервер, — телеграм-ботом, ntfy, Gotify или чем-то совсем экзотическим? И был ли случай, когда уведомление не дошло именно тогда, когда было нужнее всего?
Вы можете написать сейчас и зарегистрироваться позже.
Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.
Примечание: Ваш пост будет проверен модератором, прежде чем станет видимым.
Схема «алерты в телеграм-бота» стала настолько стандартной, что её перестали обсуждать. А обсудить есть что: токен бота размазан по переменным окружения половины ваших контейнеров, канал доставки целиком чужой, а в момент, когда бота забанят или мессенджер перестанет открываться, вы окажетесь единственным админом, который не в курсе, что у него горит сервер.
Альтернатива, требующая примерно нулевых усилий на входе, — ntfy: сервер на Go, который принимает обычный HTTP POST и превращает его в push на телефоне. Никакого SDK, никакого формата вебхука, который надо изучать:
curl -d "текст" сервер/топик— и всё. При этом сервер можно поднять свой, и тогда цепочка доставки принадлежит вам целиком. Почти целиком — с iOS есть нюанс, к которому мы дойдём.Дальше — маршрут по уровням: от «попробовать за тридцать секунд» до сервера с логинами, токенами и правами на топики. Каждый уровень самодостаточен: можно остановиться там, где вам хватит.
ℹ️ Справка
Актуальная версия на момент написания — v2.24.0 от 4 июня 2026. Проект живой, но релизы редкие: предыдущие — 2.23.0 (18 мая) и 2.22.0 (21 апреля 2026). Это нормальный ритм для инструмента, который делает одну вещь и уже её сделал.
Уровень 0. Чужой сервер и один curl
Публичный ntfy.sh не требует ни регистрации, ни ключей. Топик создаётся в момент первой отправки:
Подписываетесь на тот же топик в приложении или на
https://ntfy.sh/moi-domashnii-server-x7k2в браузере — и сообщение приходит.⛔ Так делать нельзя
Ровно здесь совершают главную ошибку. В документации это сказано прямым текстом: топик — это по сути пароль. Кто угадал имя, тот читает все ваши уведомления и может слать свои.
ntfy.sh/backup,ntfy.sh/home,ntfy.sh/alerts— это публичная лента, на которую подписан кто угодно.Имя топика ограничено 64 символами, допустимы буквы, цифры,
-и_. Если остаётесь на публичном сервере — генерируйте его как пароль:Для чего этого уровня хватает: разовые скрипты, «дай знать, когда доедет rsync», уведомления, содержимое которых не жалко показать посторонним. Для всего остального — дальше.
Уровень 1. Свой ntfy в Docker
Образ
binwiederhier/ntfyсобирается под amd64, armv6, armv7 и arm64 — то есть спокойно едет на Raspberry Pi и на любом мини-ПК.Минимальный
etc/server.yml:📌 Заметка
Внутри контейнера сервер слушает 80-й порт (
listen-http: ":80"по умолчанию), рядом естьlisten-https(:443) иlisten-unix— если вы предпочитаете отдавать сокет прокси-серверу напрямую, минуя TCP.cache-file— это SQLite, в котором лежат сообщения. Без него сервер работает целиком в памяти: перезапустили контейнер — история пропала, а подписчик, который был офлайн, ничего не получит. Практически всегда его надо задать.Уровень 2. За обратным прокси
Здесь живёт грабля, на которую наступают почти все.
⚠️ Осторожно
Если ntfy стоит за nginx, Caddy или Traefik и вы не выставили
behind-proxy: true, сервер видит всех клиентов как один IP — адрес прокси. А лимиты в ntfy считаются по «посетителю», то есть по IP. Результат: один активный скрипт съедает лимит на всех, остальные получают 429.Второе, что обязательно на этом уровне, — корректный
base-url. Он используется в ссылках на вложения и в письмах; если он не совпадает с реальным внешним адресом, вложения будут приходить со ссылками в никуда.Третье — сам прокси. ntfy держит длинные соединения для подписчиков, поэтому буферизация ответа и короткий таймаут прокси ломают доставку. В nginx это
proxy_buffering off;и увеличенныйproxy_read_timeout, в Caddy обычно достаточно обычногоreverse_proxyбез дополнительных настроек.Уровень 3. Кто кому что может
По умолчанию сервер открыт: любой, кто знает адрес, публикует и читает. Закрывается это одной строкой:
Значения
auth-default-access:read-writeread-onlywrite-onlydeny-allДальше — CLI. Команды выполняются внутри контейнера (
docker compose exec ntfy ntfy ...):Шаблон
alerts-*работает — права выдаются на группу топиков, а не только на один.🔒 Безопасность
Токен лучше пароля не потому, что он длиннее, а потому что его можно отозвать по одному, не меняя доступ везде сразу, и можно выдать со сроком жизни. Для сервиса, который только шлёт, ставьте
write-only: скомпрометированный контейнер тогда не сможет прочитать чужие уведомления.В версии 2.24.0, кстати, чинили как раз проблему в этой области — некорректное сравнение имён топиков в ACL без учёта регистра на SQLite. Если вы держите ntfy с правами доступа, обновиться стоит.
Отправка с токеном:
Уровень 4. Телефон, и почему iOS требует чужого сервера
Это самая недооценённая часть. Одно и то же приложение доставляет уведомления тремя разными способами, и от способа зависит и задержка, и приватность, и расход батареи.
Android, сборка из Google Play. Firebase используется только для топиков на самом ntfy.sh. Для ваших собственных серверов приложение Firebase не задействует вообще — оно держит прямое соединение. Это прямая цитата из документации, и она снимает главное опасение: свой сервер не «ходит через Google».
Android, сборка из F-Droid. Firebase нет в принципе, все подписки по умолчанию идут в режиме мгновенной доставки.
Мгновенная доставка — это постоянное соединение, а значит foreground-сервис и вечное уведомление в шторке. Без него, предупреждает документация, сообщения могут приходить с задержкой в минуты и даже часы. Выбор здесь честный и явный: либо иконка в статус-баре, либо непредсказуемая задержка.
❗ Главное
iOS — отдельный случай. Apple не разрешает приложениям держать постоянное соединение, всё идёт через APNS. Достучаться до APNS может только владелец сертификата приложения, то есть проект ntfy. Поэтому для своего сервера в конфиг добавляют:
Ваш сервер при поступлении сообщения отправляет на ntfy.sh короткий poll-запрос, тот будит телефон через APNS, а телефон приходит к вашему серверу за телом сообщения. Наружу уходит факт события, а не его содержимое.
Если вас и это не устраивает — остаётся вариант «зайти в веб-интерфейс и подписаться там» через Web Push, для которого нужны ключи VAPID:
Ключи генерируются командой
ntfy webpush keys. Заметьте, что Web Push в браузере тоже ходит через сервисы Google/Mozilla/Apple — полностью изолированного пути на смартфон сегодня просто не существует.Что можно положить в уведомление
Это то, ради чего ntfy стоит предпочесть самописному вебхуку: сообщение — не просто строка.
X-Titlet,tiX-Priorityp,prioX-Tagstag,taX-ClickClickX-ActionsActionX-AttachaX-MarkdownmdX-DelayDelayX-EmailEmailПриоритеты влияют на поведение телефона:
urgent/maxhighdefaultlowminСамое интересное — кнопки. Их три типа:
view(открыть ссылку),http(сделать HTTP-запрос) иbroadcast(отправить Android-интент в Tasker и подобное). Классический сценарий домашнего сервера:Одно уведомление — и действие выполняется с экрана блокировки, без открытия Home Assistant.
✅ Как правильно
Полезные мелочи, которые редко находят сразу:
X-Delayпринимает30s,5m,2hили Unix-таймстамп — готовый планировщик напоминаний без cron.Markdown: yesпревращает тело в размеченный текст: список отвалившихся сервисов читается несравнимо лучше.curl ntfy.example.org -d '{"topic":"alerts-infra","title":"…","priority":5}'— удобно, когда отправляет не bash, а код.Куда это подключается на практике
OnFailure=, который дёргает одноразовый сервис сcurl. Падение любого сервиса — сразу в телефон.curl -d "..." -H "Priority: 5"в ветке ошибки — и вы наконец узнаёте о неудавшемся бэкапе не через месяц.rest_commandили интеграциюntfy; сюда же — кнопки действий с примера выше.Лимиты, которые стоит знать заранее
Даже на своём сервере лимиты включены — они защищают от того, чтобы один взбесившийся скрипт не забил диск.
visitor-request-limit-burstvisitor-request-limit-replenishattachment-file-size-limitattachment-total-size-limitattachment-expiry-durationvisitor-attachment-total-size-limitvisitor-attachment-daily-bandwidth-limitТри часа хранения вложений — это про то, что ntfy не файлопомойка. Скриншот с камеры дойдёт, архив логов — нет, и не должен.
⚠️ Осторожно
Ещё одна деталь для тех, кто выставляет сервер наружу: в 2.23.0 добавили лимит на создание новых топиков с одного адреса — именно чтобы нельзя было перебором нащупать существующие. Если у вас открытый сервер с
auth-default-access: read-write, перебор имён топиков остаётся полностью рабочей атакой. Это ещё один аргумент заdeny-all.Чего ntfy не делает
Честности ради: это не мессенджер и не система оповещений с дежурными сменами.
Здесь нет шифрования сообщений между отправителем и получателем — трафик защищён TLS, но на сервере сообщение лежит открытым текстом в SQLite. Нет эскалаций «не подтвердил за 5 минут — звони следующему», нет расписаний дежурств, нет группировки и подавления шквала одинаковых алертов. Если вам нужно это — смотрите в сторону Alertmanager перед ntfy, а ntfy оставьте как транспорт последней мили.
И ещё: своим сервером вы берёте на себя его доступность. Уведомление о том, что сервер лёг, не придёт с сервера, который лёг. Классическое решение — держать ntfy не на той же машине, за которой он следит, а внешний watchdog вроде Healthchecks.io — на стороне.
Источники
💬 Вопрос к сообществу
Чем вы будите себя, когда падает домашний сервер, — телеграм-ботом, ntfy, Gotify или чем-то совсем экзотическим? И был ли случай, когда уведомление не дошло именно тогда, когда было нужнее всего?
TOP HOSTERS: KAMATERA (30 дней бесплатного теста!)
Универсальный хостер №1 - 4VPS.su (2Гб\с сервера) - 10% скидка на первый заказ или 15% бонус на первое пополнение