"Быстрый
</p>
<ul>
<li><strong>Сервер, Docker, скрипты</strong> — ntfy (curl в одну строку, iOS + Android, self-hosted)</li>
<li><strong>Личные алерты на телефон</strong> — <a class="wpil_keyword_link" href="https://t.me/it_apteka_com/34" target="_blank" rel="noopener" title="Telegram" data-wpil-keyword-link="linked" data-wpil-monitor-id="2387">Telegram</a> Bot API (бесплатно, без лимитов, 5 минут настройки)</li>
<li><strong>Сайт и web push для пользователей</strong> — OneSignal (до 10 000 подписчиков бесплатно)</li>
<li><strong>Мобильное приложение</strong> — Firebase Cloud Messaging (без лимитов на сообщения)</li>
<li><strong>Изолированная сеть без интернета</strong> — ntfy self-hosted или Gotify</li>
</ul>
<p>
<h2>Диагноз: почему «просто поднять push» — это не просто</h2>
<p>Push уведомления бесплатно — это реально. Но перед тем как копировать первую команду из статьи, стоит понять что именно ты выбираешь и почему.</p>
<p>Поднять полноценную push-инфраструктуру с нуля — это несколько месяцев работы. FCM требует аккаунта разработчика Google, APNs для iOS — платного Apple Developer за $99 в год, сертификатов, масштабирования. Web Push Protocol — VAPID-ключи, Service Worker, обработка подписок. Всё это уже решено за тебя в готовых сервисах.</p>
<p>Что получишь из этой статьи: сравнение семи инструментов, готовые команды под каждый сценарий, примеры интеграции с Zabbix, Uptime Kuma и <a class="wpil_keyword_link" href="https://it-apteka.com/category/virtualise/" target="_blank" rel="noopener" title="Виртуализация" data-wpil-keyword-link="linked" data-wpil-monitor-id="2379">Proxmox</a>, и честный разбор ограничений бесплатных тарифов. Время на <a href="https://it-apteka.com/n8n-ustanovka-i-nastrojka-docker-telegram-ai-agenty/" title="n8n установка и настройка: Docker, Telegram, AI-агенты" target="_blank" rel="noopener" data-wpil-monitor-id="2382">настройку — от 5 минут для Telegram</a> до 20 минут для ntfy self-hosted.</p>
<p>Что нужно: <a href="https://it-apteka.com/1243-2/" title="Сервер активации Windows (KMS) в Docker: варианты развёртывания и интеграция с Active Directory" target="_blank" rel="noopener" data-wpil-monitor-id="2383">сервер или VPS под self-hosted варианты</a>, Docker (опционально), телефон с Android или iOS.</p>
<h2>Как работают push уведомления: схема за 60 секунд</h2>
<p>Push уведомление — это сообщение, которое сервер отправляет на устройство без запроса со стороны клиента. Устройство не поллит сервер каждые N секунд. Вместо этого держит постоянное соединение с push-провайдером, который и доставляет сообщения.</p>
<p>На практике цепочка выглядит так:</p>
<pre class="mermaid">
%%{init: {
'theme': 'base',
'themeVariables': {
'primaryColor': '#ffffff',
'primaryTextColor': '#1e293b',
'primaryBorderColor': '#94a3b8',
'lineColor': '#64748b',
'fontSize': '15px',
'fontFamily': 'ui-sans-serif, system-ui, sans-serif'
},
'flowchart': {'curve': 'linear', 'nodeSpacing': 50, 'rankSpacing': 50}
}}%%
flowchart TD
A["Твой сервер или скрипт"] --> B["Push-провайдер"]
B --> C["FCM - Android"]
B --> D["APNs - iOS"]
B --> E["WebSocket - браузер"]
B --> F["Long-poll - self-hosted"]
style A fill:#f8fafc,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style B fill:#f8fafc,stroke:#f97316,stroke-width:2px,color:#9a3412
style C fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
style D fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
style E fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
style F fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
</pre>
<p>Для Android постоянное TCP-соединение держит Google через FCM. Для iOS — Apple через APNs. Для браузеров — Web Push Protocol с VAPID-ключами. Self-hosted решения типа ntfy и Gotify используют Server-Sent Events или WebSocket и держат соединение сами.</p>
<p>Ключевой момент: на Android и iOS ты не можешь доставить push напрямую со своего сервера на устройство. Всегда идёшь через FCM или APNs — либо напрямую, либо через промежуточный сервис. ntfy и Gotify используют FCM/APNs под капотом для своих мобильных приложений.</p>
<h2>Сравнение бесплатных push-сервисов: таблица</h2>
<table>
<thead>
<tr>
<th>Сервис</th>
<th>Self-hosted</th>
<th>Android</th>
<th>iOS</th>
<th>Браузер</th>
<th>API</th>
<th>Бесплатно</th>
<th>Лучший сценарий</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://ntfy.sh" target="_blank" rel="noopener noreferrer">ntfy</a></td>
<td>Да</td>
<td>Да</td>
<td>Да</td>
<td>Да</td>
<td>curl, REST</td>
<td>Полностью</td>
<td>Серверы, скрипты, <a class="wpil_keyword_link" href="https://it-apteka.com/category/monitoring/" target="_blank" rel="noopener" title="Мониторинг" data-wpil-keyword-link="linked" data-wpil-monitor-id="2381">мониторинг</a></td>
</tr>
<tr>
<td><a href="https://gotify.net" target="_blank" rel="noopener noreferrer">Gotify</a></td>
<td>Да</td>
<td>Да</td>
<td>Нет</td>
<td>Да</td>
<td>REST</td>
<td>Полностью</td>
<td>DevOps, домашний сервер</td>
</tr>
<tr>
<td><a href="https://onesignal.com" target="_blank" rel="noopener noreferrer">OneSignal</a></td>
<td>Нет</td>
<td>Да</td>
<td>Да</td>
<td>Да</td>
<td>REST</td>
<td>До 10 000 подписчиков</td>
<td>Сайты, маркетинг, web push</td>
</tr>
<tr>
<td><a href="https://firebase.google.com/products/cloud-messaging" target="_blank" rel="noopener noreferrer">Firebase FCM</a></td>
<td>Нет</td>
<td>Да</td>
<td>Да</td>
<td>Да</td>
<td>REST, SDK</td>
<td>Без лимита сообщений</td>
<td>Мобильные приложения</td>
</tr>
<tr>
<td><a href="https://pushover.net" target="_blank" rel="noopener noreferrer">Pushover</a></td>
<td>Нет</td>
<td>Да</td>
<td>Да</td>
<td>Нет</td>
<td>Да</td>
<td>Пробный период, потом $5 разово</td>
<td>Личные алерты</td>
</tr>
<tr>
<td>Telegram Bot API</td>
<td>Нет (облако Telegram)</td>
<td>Да</td>
<td>Да</td>
<td>Нет</td>
<td>REST</td>
<td>Полностью</td>
<td>Личные и командные алерты</td>
</tr>
<tr>
<td><a href="https://github.com/caronc/apprise" target="_blank" rel="nofollow noopener noreferrer">Apprise</a></td>
<td>Да</td>
<td>Через ntfy</td>
<td>Через ntfy</td>
<td>Нет</td>
<td>CLI, REST</td>
<td>Полностью</td>
<td>Агрегатор, 80+ каналов</td>
</tr>
</tbody>
</table>
<h2>Что выбрать под конкретную задачу</h2>
<table>
<thead>
<tr>
<th>Сценарий</th>
<th>Что выбрать</th>
<th>Почему именно это</th>
</tr>
</thead>
<tbody>
<tr>
<td>Домашний сервер, <a class="wpil_keyword_link" href="https://it-apteka.com/tag/docker/" target="_blank" rel="noopener" title="Docker" data-wpil-keyword-link="linked" data-wpil-monitor-id="2388">Docker</a>, Proxmox</td>
<td>ntfy или Gotify</td>
<td>Self-hosted, curl в одну строку, нет облака</td>
</tr>
<tr>
<td>Сайт и web push для аудитории</td>
<td>OneSignal</td>
<td>Готовый виджет подписки, аналитика, бесплатно до 10k</td>
</tr>
<tr>
<td>Мобильное приложение Android/iOS</td>
<td>Firebase Cloud Messaging</td>
<td>Канонический способ, без лимитов на сообщения</td>
</tr>
<tr>
<td>Личные уведомления на телефон</td>
<td>Telegram Bot API</td>
<td>Бесплатно, без регистрации сервисов, 5 минут</td>
</tr>
<tr>
<td>Zabbix, Grafana, Uptime Kuma</td>
<td>ntfy или Telegram</td>
<td>Встроенная интеграция, webhook, curl</td>
</tr>
<tr>
<td>Изолированная сеть без интернета</td>
<td>ntfy self-hosted или Gotify</td>
<td>Работают полностью локально</td>
</tr>
<tr>
<td>Нужно покрыть несколько каналов сразу</td>
<td>Apprise</td>
<td>Slack, Telegram, ntfy, email — всё в одной команде</td>
</tr>
</tbody>
</table>
<h2>ntfy: самый простой push для серверов</h2>
<h3>Что это и почему именно ntfy</h3>
<p><a href="https://ntfy.sh" target="_blank" rel="noopener noreferrer">ntfy</a> — open-source push-сервис, который работает по принципу pub/sub через HTTP. Отправляешь POST-запрос на URL с именем топика — уведомление приходит на все подписанные устройства. Никакой регистрации. Никаких токенов приложений. Никакого SDK.</p>
<p>Есть публичный сервер ntfy.sh — бесплатно, но топики публичны по умолчанию. Для приватных алертов — разворачивай свой экземпляр. На это уходит 3 минуты с Docker.</p>
<p>Почему ntfy, а не Gotify: iOS-клиент. У Gotify официального iOS-приложения нет. Если в команде есть хоть один iPhone — выбор за ntfy.</p>
<h3>Системные требования</h3>
<table>
<thead>
<tr>
<th>Компонент</th>
<th>Минимум</th>
<th>Рекомендуется</th>
</tr>
</thead>
<tbody>
<tr>
<td>ОС</td>
<td>Linux (любой дистрибутив), <a class="wpil_keyword_link" href="https://it-apteka.com/category/windows-server/" target="_blank" rel="noopener" title="Windows Server" data-wpil-keyword-link="linked" data-wpil-monitor-id="2377">Windows</a> (WSL2)</td>
<td>Ubuntu 22.04 LTS / <a class="wpil_keyword_link" href="https://it-apteka.com/tag/debian/" target="_blank" rel="noopener" title="Debian" data-wpil-keyword-link="linked" data-wpil-monitor-id="2375">Debian</a> 12</td>
</tr>
<tr>
<td>Docker</td>
<td>20.10+</td>
<td>24.x+</td>
</tr>
<tr>
<td>RAM</td>
<td>64 MB</td>
<td>256 MB</td>
</tr>
<tr>
<td>Диск</td>
<td>100 MB</td>
<td>1 GB (под кэш сообщений)</td>
</tr>
<tr>
<td>ntfy</td>
<td>v2.x</td>
<td>Актуальная версия — проверяй на <a href="https://github.com/binwiederhier/ntfy/releases" target="_blank" rel="nofollow noopener noreferrer">GitHub Releases</a></td>
</tr>
</tbody>
</table>
<p>На момент публикации актуальна версия ntfy v2.11.0. Перед установкой проверь свежие релизы на GitHub.</p>
<h3>Установка ntfy через Docker Compose</h3>
<p>Создай директории и файл конфига:</p>
<pre><code class="language-bash">
mkdir -p /etc/ntfy /var/cache/ntfy
</code></pre>
<p>Файл <code>/etc/ntfy/server.yml</code>:</p>
<pre><code class="language-text">
base-url: https://ntfy.yourdomain.com
auth-file: /var/cache/ntfy/auth.db
auth-default-access: deny-all
behind-proxy: true
cache-file: /var/cache/ntfy/cache.db
cache-duration: 24h
</code></pre>
<p>Docker Compose файл <code>docker-compose.yml</code>:</p>
<pre><code class="language-text">
services:
ntfy:
image: binwiederhier/ntfy
command: serve
ports:
- "8080:80"
volumes:
- /etc/ntfy:/etc/ntfy
- /var/cache/ntfy:/var/cache/ntfy
environment:
- TZ=Europe/Moscow
restart: unless-stopped
</code></pre>
<pre><code class="language-bash">
docker compose up -d
# Создать пользователя
docker exec -it ntfy ntfy user add --role=user myuser
# Дать права на топик
docker exec -it ntfy ntfy access myuser alerts rw
# Проверить что сервер живой
curl -s http://localhost:8080/health
# Должен вернуть: {"healthy":true}
</code></pre>
<h3>Таблица портов ntfy</h3>
<table>
<thead>
<tr>
<th>Порт</th>
<th>Протокол</th>
<th>Назначение</th>
<th>Снаружи</th>
</tr>
</thead>
<tbody>
<tr>
<td>8080</td>
<td>TCP</td>
<td>HTTP API, веб-интерфейс</td>
<td>Через reverse proxy (443)</td>
</tr>
<tr>
<td>443</td>
<td>TCP</td>
<td>HTTPS через Nginx/Caddy</td>
<td>Да</td>
</tr>
</tbody>
</table>
<h3>Отправка уведомления через curl</h3>
<pre><code class="language-bash">
# Простое уведомление на публичный сервер
curl -d "Backup completed" https://ntfy.sh/myserver-alerts
# С заголовком, приоритетом и эмодзи-тегом
curl -H "Title: Backup Done" \
-H "Priority: high" \
-H "Tags: white_check_mark" \
-d "Backup of db01 completed successfully" \
https://ntfy.sh/myserver-alerts
# На свой сервер с авторизацией
curl -u myuser:mypassword \
-d "Disk usage > 90% on node01" \
https://ntfy.yourdomain.com/alerts
</code></pre>
<p>Приоритеты: min, low, default, high, urgent. При urgent — телефон вибрирует даже в режиме «Не беспокоить».</p>
<h3>Отправка из PowerShell</h3>
<pre><code class="language-powershell">
# Уведомление из Windows-скрипта
Invoke-RestMethod -Uri "https://ntfy.sh/myserver-alerts" `
-Method Post `
-Body "Windows Update completed on WS2022-01"
# С заголовком и приоритетом
Invoke-RestMethod -Uri "https://ntfy.sh/myserver-alerts" `
-Method Post `
-Headers @{
"Title" = "Update Done"
"Priority" = "high"
} `
-Body "Patch Tuesday done, reboot scheduled"
</code></pre>
<h3>Nginx reverse proxy для ntfy</h3>
<pre><code class="language-text">
server {
listen 443 ssl;
server_name ntfy.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/ntfy.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ntfy.yourdomain.com/privkey.pem;
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;
# Обязательно для SSE - иначе уведомления будут приходить с задержкой
proxy_buffering off;
proxy_read_timeout 3600s;
}
}
</code></pre>
<h3>Интеграция ntfy с Uptime Kuma</h3>
<p>В Uptime Kuma перейди в Settings — Notifications — Add Notification. Выбери тип ntfy. Укажи:</p>
<ul>
<li>Server URL: <code>https://ntfy.sh</code> или твой сервер</li>
<li>Topic: имя твоего топика</li>
<li>Priority: 4 для обычных, 5 для критических</li>
</ul>
<p>Uptime Kuma отправляет уведомления при падении сервиса и при восстановлении. Работает из коробки, без скриптов.</p>
<h3>Подписка на уведомления</h3>
<p>Android: <a href="https://play.google.com/store/apps/details?id=io.heckel.ntfy" target="_blank" rel="noopener noreferrer">ntfy из Google Play</a>. iOS: <a href="https://apps.apple.com/us/app/ntfy/id1625396347" target="_blank" rel="noopener noreferrer">ntfy из App Store</a>. Браузер: зайди на ntfy.sh или свой сервер и подпишись на топик через веб-интерфейс. Укажи имя топика — всё, подписан.</p>
<h2>Gotify: self-hosted с историей и веб-интерфейсом</h2>
<h3>Чем Gotify отличается от ntfy</h3>
<p><a href="https://gotify.net" target="_blank" rel="noopener noreferrer">Gotify</a> — self-hosted push-сервер с веб-интерфейсом, REST API и WebSocket. Требует создания приложения и токена — чуть больше настройки. Зато история сообщений хранится без ограничений по времени, есть поиск, управление несколькими приложениями через один сервер.</p>
<p>iOS официально не поддерживается — нет APNs-интеграции. Если нужен iPhone — используй ntfy. Если все на Android или только браузер — Gotify подходит отлично.</p>
<h3>Установка через Docker Compose</h3>
<pre><code class="language-text">
services:
gotify:
image: gotify/server
ports:
- "8080:80"
volumes:
- /var/gotify/data:/app/data
environment:
- GOTIFY_DEFAULTUSER_PASS=changeme
restart: unless-stopped
</code></pre>
<pre><code class="language-bash">
docker compose up -d
# Открой http://localhost:8080
# Войди: admin / changeme - сразу смени пароль
# Создай приложение: Apps - Create Application
# Скопируй токен - он понадобится для отправки
</code></pre>
<h3>Отправка уведомления через curl</h3>
<pre><code class="language-bash">
# Отправить сообщение через токен приложения
curl -X POST "https://gotify.yourdomain.com/message" \
-H "X-Gotify-Key: YOUR_APP_TOKEN" \
-H "Content-Type: application/json" \
-d '{"title":"Backup Done","message":"db01 backup completed","priority":5}'
# Из bash-скрипта с переменными
GOTIFY_URL="https://gotify.yourdomain.com"
GOTIFY_TOKEN="YOUR_APP_TOKEN"
MESSAGE="Disk usage on node01: 87%"
curl -s -X POST "$GOTIFY_URL/message" \
-H "X-Gotify-Key: $GOTIFY_TOKEN" \
-F "title=Disk Alert" \
-F "message=$MESSAGE" \
-F "priority=7"
</code></pre>
<p>Priority в Gotify: 1-3 низкий, 4-6 средний, 7-9 высокий, 10 критический. Высокий приоритет — телефон не игнорирует уведомление даже при режиме тишины.</p>
<h3>Интеграция Gotify с Zabbix</h3>
<p>В Zabbix настрой Media Type — Webhook. URL: <code>https://gotify.yourdomain.com/message</code>. Метод: POST. Заголовок: <code>X-Gotify-Key: {TOKEN}</code>. Тело:</p>
<pre><code class="language-text">
{
"title": "{TRIGGER.NAME}",
"message": "Host: {HOST.NAME}\nStatus: {TRIGGER.STATUS}\nSeverity: {TRIGGER.SEVERITY}",
"priority": 7
}
</code></pre>
<p>Gotify + Proxmox 8.1: поддержка встроена нативно. Идёшь в Datacenter — Notifications — Add — Gotify. Указываешь URL и токен — всё. Никаких скриптов.</p>
<h2>Telegram Bot API: быстрее всего для личных алертов</h2>
<h3>Почему Telegram часто удобнее специализированного push-сервиса</h3>
<p>Telegram Bot API — бесплатный, без регистрации сервисов, без лимитов для личных ботов. Уведомление приходит как обычное сообщение — история сохраняется, поиск работает, можно переслать коллеге.</p>
<p>Для командных алертов особенно удобно: создаёшь группу, добавляешь бота — все в команде видят алерты в одном месте. Никакого отдельного мобильного приложения. Никакой дополнительной настройки у каждого сотрудника.</p>
<p>Минус один: нет web push. Для уведомлений на сайте через браузер Telegram не подходит.</p>
<h3>Создание бота: 5 минут</h3>
<ol>
<li>Напиши <code>@BotFather</code> в Telegram</li>
<li>Отправь <code>/newbot</code>, придумай имя и username</li>
<li>Получи токен вида <code>123456789:AAF_xxxxxxxx</code> — сохрани его</li>
<li>Напиши боту любое сообщение чтобы начать чат</li>
<li>Узнай свой chat_id — открой в браузере: <code>https://api.telegram.org/botТВОЙ_ТОКЕН/getUpdates</code></li>
<li>Найди поле <code>"chat":{"id":...</code> — это и есть твой chat_id</li>
</ol>
<h3>Отправка через curl</h3>
<pre><code class="language-bash">
# Простое уведомление
curl -s -X POST "https://api.telegram.org/bot123456789:AAF_xxx/sendMessage" \
-d chat_id="YOUR_CHAT_ID" \
-d text="Proxmox backup failed on pve01"
# С Markdown форматированием
curl -s -X POST "https://api.telegram.org/botTOKEN/sendMessage" \
-d chat_id="CHAT_ID" \
-d parse_mode="Markdown" \
-d text="*ALERT* - Disk usage on _node01_ is *92%*"
# Функция для переиспользования в скриптах
send_telegram() {
local message="$1"
curl -s -X POST "https://api.telegram.org/botTOKEN/sendMessage" \
-d chat_id="CHAT_ID" \
-d text="$message" > /dev/null
}
# Пример использования
send_telegram "Backup completed: $(date)"
</code></pre>
<h3>Из PowerShell</h3>
<pre><code class="language-powershell">
$Token = "123456789:AAF_xxxxxxxxxxxxxxxx"
$ChatId = "YOUR_CHAT_ID"
$Message = "Windows backup completed on $(hostname)"
Invoke-RestMethod -Uri "https://api.telegram.org/bot$Token/sendMessage" `
-Method Post `
-Body @{
chat_id = $ChatId
text = $Message
}
</code></pre>
<h3>Telegram в Proxmox (bash-скрипт)</h3>
<pre><code class="language-bash">
# /usr/local/bin/notify-telegram.sh
#!/bin/bash
TOKEN="YOUR_BOT_TOKEN"
CHAT_ID="YOUR_CHAT_ID"
MESSAGE="$1"
curl -s -X POST "https://api.telegram.org/bot$TOKEN/sendMessage" \
-d chat_id="$CHAT_ID" \
-d text="[Proxmox] $MESSAGE"
</code></pre>
<pre><code class="language-bash">
chmod +x /usr/local/bin/notify-telegram.sh
# Проверка
/usr/local/bin/notify-telegram.sh "Test alert from pve01"
</code></pre>
<p>Для vzdump: в конфиге <code>/etc/vzdump.conf</code> добавь строку <code>script: /usr/local/bin/notify-telegram.sh</code>. После каждого бэкапа <a class="wpil_keyword_link" href="https://it-apteka.com/category/scripts/" target="_blank" rel="noopener" title="Скрипты" data-wpil-keyword-link="linked" data-wpil-monitor-id="2374">скрипт</a> получит статус как аргумент.</p>
<h2>OneSignal: web push для сайтов и маркетинга</h2>
<h3>Что OneSignal умеет бесплатно</h3>
<p>На бесплатном тарифе OneSignal даёт: до 10 000 активных подписчиков, неограниченное количество уведомлений, сегментацию аудитории, A/B-тестирование, аналитику доставки и кликов. Это реальные возможности без платы — не урезанная демо-версия.</p>
<p>Брендинг OneSignal присутствует в prompt подписки, но не в самих уведомлениях. Пользователь видит уведомление от твоего сайта.</p>
<h3>Интеграция для сайта</h3>
<ol>
<li>Зарегистрируйся на <a href="https://onesignal.com" target="_blank" rel="noopener noreferrer">onesignal.com</a></li>
<li>Создай приложение — выбери Web Push</li>
<li>Укажи домен сайта</li>
<li>Скопируй App ID</li>
</ol>
<pre><code class="language-bash">
<!-- Вставь в <head> -->
<script src="https://cdn.onesignal.com/sdks/web/v16/OneSignalSDK.page.js" defer></script>
<script>
window.OneSignalDeferred = window.OneSignalDeferred || [];
OneSignalDeferred.push(async function(OneSignal) {
await OneSignal.init({
appId: "YOUR_APP_ID",
});
});
</script>
</code></pre>
<p>После вставки браузер покажет системный prompt на разрешение уведомлений. Пользователи, которые согласятся, начнут получать push.</p>
<h3>Отправка через OneSignal REST API</h3>
<pre><code class="language-bash">
# Отправить всем подписчикам
curl -X POST "https://onesignal.com/api/v1/notifications" \
-H "Content-Type: application/json" \
-H "Authorization: Basic YOUR_REST_API_KEY" \
-d '{
"app_id": "YOUR_APP_ID",
"included_segments": ["All"],
"contents": {"en": "Новая статья опубликована"},
"headings": {"en": "IT-Apteka.com"}
}'
# Только подписчикам с тегом plan:pro
curl -X POST "https://onesignal.com/api/v1/notifications" \
-H "Content-Type: application/json" \
-H "Authorization: Basic YOUR_REST_API_KEY" \
-d '{
"app_id": "YOUR_APP_ID",
"filters": [{"field": "tag", "key": "plan", "relation": "=", "value": "pro"}],
"contents": {"en": "Обновление для PRO доступно"},
"headings": {"en": "Обновление"}
}'
</code></pre>
<h2>Firebase Cloud Messaging: для мобильных приложений</h2>
<h3>Когда FCM, а не ntfy</h3>
<p>Firebase Cloud Messaging — канонический способ доставки push на Android. Если пишешь мобильное приложение — идёшь через FCM. Для серверных алертов и скриптов FCM избыточен: требует проекта в Firebase, Service Account, JSON-ключей. ntfy или Telegram проще в два раза.</p>
<p>Лимиты по сообщениям фактически отсутствуют — FCM бесплатен без явных ограничений на количество устройств и сообщений.</p>
<h3>Отправка через FCM HTTP API v1</h3>
<pre><code class="language-bash">
# Получить access token через Service Account
ACCESS_TOKEN=$(gcloud auth print-access-token)
# Отправить уведомление на конкретное устройство
curl -X POST \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"message": {
"token": "DEVICE_FCM_TOKEN",
"notification": {
"title": "Сервер упал",
"body": "Проверь nginx на node01"
}
}
}' \
"https://fcm.googleapis.com/v1/projects/YOUR_PROJECT_ID/messages:send"
</code></pre>
"Legacy
<br />
Google объявил об отключении Legacy HTTP API. Если пишешь новую интеграцию — используй FCM HTTP API v1 сразу. Legacy работает, но дата отключения уже перенесена несколько раз — когда-нибудь выключат без предупреждения.<br />
<h2>Apprise: один агрегатор для всех каналов</h2>
<h3>Зачем нужен агрегатор</h3>
<p><a href="https://github.com/caronc/apprise" target="_blank" rel="nofollow noopener noreferrer">Apprise</a> — open-source библиотека и CLI, который подключает более 80 сервисов уведомлений через единый интерфейс. Telegram, ntfy, Gotify, Slack, Discord, email, Matrix, PagerDuty — всё через одну команду или строку конфига.</p>
<p>Полезно когда: нужно отправлять алерт одновременно в Telegram и на email, или переключаться между провайдерами без правки скриптов.</p>
<pre><code class="language-bash">
# Установка
pip install apprise
# Уведомление в Telegram
apprise -b "Backup completed" "tgram://BOT_TOKEN/CHAT_ID"
# Одновременно в Telegram и ntfy
apprise -b "Server alert" \
"tgram://BOT_TOKEN/CHAT_ID" \
"ntfy://ntfy.sh/myserver"
# Из конфига
apprise -b "Backup done" --config /etc/apprise.yml
</code></pre>
<p>Файл <code>/etc/apprise.yml</code>:</p>
<pre><code class="language-text">
urls:
- tgram://BOT_TOKEN/CHAT_ID
- ntfy://ntfy.sh/myserver-alerts
- gotify://gotify.yourdomain.com/APP_TOKEN
</code></pre>
<p>Apprise встроен в Uptime Kuma, Homer и Healthchecks — если уже используешь эти инструменты, Apprise уже доступен без отдельной установки.</p>
<h2>Практические примеры для серверного мониторинга</h2>
<h3>Пример 1: уведомление из bash-скрипта при завершении бэкапа</h3>
<pre><code class="language-bash">
#!/bin/bash
# /usr/local/bin/backup-db.sh
BACKUP_DIR="/var/backups/postgres"
DATE=$(date +%Y%m%d_%H%M)
BACKUP_FILE="$BACKUP_DIR/pg_dump_$DATE.sql.gz"
pg_dump mydb | gzip > "$BACKUP_FILE"
if [ $? -eq 0 ]; then
SIZE=$(du -sh "$BACKUP_FILE" | cut -f1)
curl -s \
-H "Title: Backup OK" \
-H "Tags: white_check_mark" \
-d "pg_dump OK: $BACKUP_FILE ($SIZE)" \
https://ntfy.sh/myserver-alerts
else
curl -s \
-H "Title: BACKUP FAILED" \
-H "Priority: urgent" \
-H "Tags: rotating_light" \
-d "pg_dump returned error on $(hostname) at $(date)" \
https://ntfy.sh/myserver-alerts
fi
</code></pre>
<h3>Пример 2: мониторинг диска с алертом в Telegram</h3>
<pre><code class="language-bash">
#!/bin/bash
# Запускать через cron: */15 * * * * /usr/local/bin/disk-check.sh
THRESHOLD=85
TELEGRAM_TOKEN="YOUR_TOKEN"
TELEGRAM_CHAT="YOUR_CHAT_ID"
df -h | grep -vE '^Filesystem|tmpfs|cdrom' | awk '{ print $5 " " $1 }' | while read usage mount; do
PERCENT=$(echo $usage | sed 's/%//')
if [ "$PERCENT" -ge "$THRESHOLD" ]; then
MSG="DISK ALERT: $mount is ${PERCENT}% full on $(hostname)"
curl -s -X POST "https://api.telegram.org/bot$TELEGRAM_TOKEN/sendMessage" \
-d chat_id="$TELEGRAM_CHAT" \
-d text="$MSG"
fi
done
</code></pre>
<h3>Пример 3: функция Send-NtfyAlert в PowerShell</h3>
<pre><code class="language-powershell">
function Send-NtfyAlert {
param(
[string]$Message,
[string]$Title = "Server Alert",
[string]$Priority = "default",
[string]$Topic = "myserver-alerts"
)
Invoke-RestMethod -Uri "https://ntfy.sh/$Topic" `
-Method Post `
-Headers @{
"Title" = $Title
"Priority" = $Priority
} `
-Body $Message
}
# Использование в скрипте обслуживания
try {
Start-Process "robocopy" -ArgumentList "C:\Data \\backup\Data /MIR" -Wait
Send-NtfyAlert -Message "Robocopy completed on $(hostname)" -Title "Backup Done"
} catch {
Send-NtfyAlert -Message "Backup FAILED: $($_.Exception.Message)" `
-Title "Backup Error" `
-Priority "urgent"
}
</code></pre>
<h3>Пример 4: Uptime Kuma + Gotify</h3>
<p>В Uptime Kuma: Settings — Notifications — Add Notification — выбери Gotify. Укажи:</p>
<ul>
<li>Server URL: <code>https://gotify.yourdomain.com</code></li>
<li>Application Token: токен из Gotify веб-интерфейса</li>
<li>Priority: 7 для критических алертов</li>
</ul>
<p>Gotify хранит полную историю уведомлений с временными метками. Можно открыть веб-интерфейс и посмотреть хронологию инцидентов — что упало, когда восстановилось.</p>
<h3>Пример 5: уведомление из cron одной строкой</h3>
<pre><code class="language-bash">
# В crontab - уведомление при завершении задачи
0 3 * * * /usr/local/bin/backup.sh && \
curl -s -d "Backup OK $(date)" https://ntfy.sh/myserver || \
curl -s -H "Priority: urgent" -d "Backup FAILED $(date)" https://ntfy.sh/myserver
</code></pre>
<h2>Ограничения бесплатных тарифов: что реально режет</h2>
<table>
<thead>
<tr>
<th>Сервис</th>
<th>Лимит подписчиков</th>
<th>Лимит сообщений</th>
<th>История</th>
<th>SLA</th>
<th>White-label</th>
</tr>
</thead>
<tbody>
<tr>
<td>ntfy.sh (публичный)</td>
<td>Нет</td>
<td>250/день на IP</td>
<td>12 часов</td>
<td>Нет</td>
<td>Self-hosted</td>
</tr>
<tr>
<td>Gotify (self-hosted)</td>
<td>Нет</td>
<td>Нет</td>
<td>Диск</td>
<td>Твой сервер</td>
<td>Да</td>
</tr>
<tr>
<td>OneSignal</td>
<td>10 000</td>
<td>Нет</td>
<td>30 дней</td>
<td>Нет</td>
<td>Нет</td>
</tr>
<tr>
<td>Firebase FCM</td>
<td>Нет</td>
<td>Rate limit по скорости</td>
<td>Нет</td>
<td>Нет</td>
<td>Нет</td>
</tr>
<tr>
<td>Pushover</td>
<td>Нет</td>
<td>10 000/мес на trial</td>
<td>7 дней</td>
<td>Нет</td>
<td>Нет</td>
</tr>
<tr>
<td>Telegram Bot API</td>
<td>Нет</td>
<td>30 сообщ/сек на бота</td>
<td>В Telegram</td>
<td>Нет</td>
<td>Нет</td>
</tr>
</tbody>
</table>
<p>Что реально бьёт по рукам:</p>
<p><strong>Нет SLA ни у одного бесплатного облачного сервиса.</strong> Для критических алертов это проблема. Если ntfy.sh лежит — ты не знаешь что твой сервер упал. Для критического мониторинга — только self-hosted или резервный канал (например, Telegram как дублирующий канал к ntfy).</p>
<p><strong>Короткая история на ntfy.sh.</strong> 12 часов. Ночные события можешь не увидеть утром. Self-hosted ntfy или Gotify хранят столько сколько хватает диска.</p>
<p><strong>Рейт-лимиты Telegram.</strong> 30 сообщений/сек на бота, 20 сообщений/мин в один чат. При массовых рассылках из мониторинга — бот начнёт получать 429 Too Many Requests. Решение: батчинг алертов или дедупликация на стороне отправителя.</p>
<p><strong>Лимит подписчиков OneSignal.</strong> 10 000 — это много для небольшого сайта. Но при росте платный тариф начинается от $9/месяц. Планируй заранее если проект масштабируется.</p>
<h2>Безопасность push уведомлений</h2>
<p>Push уведомления часто содержат чувствительные данные. IP-адреса серверов. Имена хостов. Тексты ошибок с путями к файлам. Всё это летит через серверы третьих сторон.</p>
"Что
<br />
Пароли, токены, ключи API — никогда. Персональные данные пользователей — нарушение GDPR. Полные трейсбэки с внутренними путями и именами переменных — помогает атакующему. Строки подключения к базам данных — особенно.<br />
<p><strong>Чувствительные алерты — только self-hosted.</strong> ntfy или Gotify на своём сервере. Данные не покидают инфраструктуру.</p>
<p><strong>Только HTTPS для своего push-сервера.</strong> <a href="https://it-apteka.com/acme-nginx-nastrojka-let-s-encrypt-vruchnuju-i-cherez-nginx-proxy-manager/" title="ACME + Nginx: настройка Let’s Encrypt вручную и через Nginx Proxy Manager" target="_blank" rel="noopener" data-wpil-monitor-id="2384">Nginx как reverse proxy с сертификатом Let’s</a> Encrypt. Без HTTPS — уведомления читаемы в <a class="wpil_keyword_link" href="https://it-apteka.com/category/networks/" target="_blank" rel="noopener" title="Сети" data-wpil-keyword-link="linked" data-wpil-monitor-id="2378">сети</a>.</p>
<p><strong>Авторизация обязательна.</strong> Публичные топики ntfy.sh доступны всем. На своём сервере включай <code>auth-default-access: deny-all</code>.</p>
<p><strong>Не публикуй endpoint в открытом репозитории.</strong> URL вида <code>ntfy.sh/company-prod-alerts</code> в публичном GitHub — это подарок любому желающему флудить в твой топик.</p>
<p><strong>Ограничивай IP для токенов.</strong> В Gotify и в Telegram bot webhook настраивай allowlist IP откуда принимаются запросы.</p>
<h3>Схема безопасной self-hosted архитектуры</h3>
<pre class="mermaid">
%%{init: {
'theme': 'base',
'themeVariables': {
'primaryColor': '#ffffff',
'primaryTextColor': '#1e293b',
'primaryBorderColor': '#94a3b8',
'lineColor': '#64748b',
'fontSize': '15px',
'fontFamily': 'ui-sans-serif, system-ui, sans-serif'
},
'flowchart': {'curve': 'linear', 'nodeSpacing': 50, 'rankSpacing': 50}
}}%%
flowchart TD
A["Скрипт или мониторинг"] --> B["Nginx HTTPS + auth"]
B --> C["ntfy self-hosted"]
C --> D["FCM - Google"]
C --> E["APNs - Apple"]
D --> F["Android"]
E --> G["iOS"]
H["fail2ban"] --> B
style A fill:#f8fafc,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style B fill:#f8fafc,stroke:#f97316,stroke-width:2px,color:#9a3412
style C fill:#f8fafc,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style D fill:#f8fafc,stroke:#94a3b8,stroke-width:2px,color:#475569
style E fill:#f8fafc,stroke:#94a3b8,stroke-width:2px,color:#475569
style F fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
style G fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
style H fill:#f8fafc,stroke:#ef4444,stroke-width:2px,color:#dc2626
</pre>
<h2>Почему web push раздражает пользователей и как не стать частью проблемы</h2>
<p>Web push — технически правильная вещь. Сайт может уведомить пользователя о новой статье или важном событии без email. Но в руках маркетологов это превратилось в помойку — 95% пользователей кликают «Заблокировать» при виде prompt подписки.</p>
<p>Как убивают доверие к web push: показывают prompt при первом посещении без контекста, отправляют 5-10 уведомлений в день, используют кликбейт в заголовках, имитируют системные предупреждения.</p>
<p>Как делать правильно: запрашивай разрешение после конкретного действия пользователя — регистрации, заказа, нескольких прочитанных статей. Объясняй что именно он получит. Давай простой способ отписаться. Не отправляй больше 1-2 уведомлений в день. Delivery rate 50-60% при таком подходе против 5-10% при агрессивных промптах. Одно уведомление которое открыли всегда лучше десяти которые заблокировали.</p>
<h2>Резервное копирование и профилактика</h2>
<p>Self-hosted push-сервер — это инфраструктура. Относись к нему как к инфраструктуре.</p>
<p><strong>Что бэкапить:</strong> для ntfy — <code>/var/cache/ntfy/cache.db</code> и <code>/var/cache/ntfy/auth.db</code>, конфиг <code>/etc/ntfy/server.yml</code>. Для Gotify — директорию <code>/var/gotify/data</code> целиком.</p>
<p><strong>Как часто:</strong> ежедневно для auth.db (пользователи и права), еженедельно для cache.db (история сообщений). Конфиг — в <a class="wpil_keyword_link" href="https://it-apteka.com/tag/git/" target="_blank" rel="noopener" title="Git" data-wpil-keyword-link="linked" data-wpil-monitor-id="2376">git</a>-репозиторий, изменения после каждой правки.</p>
<p><strong>Как проверить работу:</strong></p>
<pre><code class="language-bash">
# Проверка ntfy
curl -s http://localhost:8080/health
# Ожидаем: {"healthy":true}
# Тестовое уведомление
curl -d "Health check $(date)" https://ntfy.yourdomain.com/monitoring
# Проверка Gotify
curl -s -H "X-Gotify-Key: TOKEN" https://gotify.yourdomain.com/health
</code></pre>
<p><strong>Автозапуск:</strong> оба сервиса запускаются через Docker с <code>restart: unless-stopped</code> — это уже покрывает перезагрузку хоста. Docker daemon должен быть включён в systemd:</p>
<pre><code class="language-bash">
systemctl enable docker
systemctl status docker
</code></pre>
<p><strong>Мониторинг самого push-сервера:</strong> добавь URL health endpoint в Uptime Kuma. Получится ситуация когда Uptime Kuma мониторит ntfy, а ntfy отправляет алерты от Uptime Kuma. Классика.</p>
<h2>Troubleshooting</h2>
<h3>ntfy: сообщения не приходят на телефон</h3>
"Ошибка:
<br />
Сначала проверь <a href="https://it-apteka.com/dns-over-tls-nastrojka-dot-na-android-keenetic-i-v-nulls-proxy/" title="DNS over TLS: настройка DoT на Android, Keenetic и в Nulls Proxy" target="_blank" rel="noopener" data-wpil-monitor-id="2386">настройки батареи на Android</a>. Android агрессивно убивает фоновые процессы — ntfy может не получать push при заблокированном экране.<br />
<pre><code class="language-bash">
# Проверить что сообщение дошло до сервера
curl -s "https://ntfy.sh/myserver-alerts/json?poll=1" | head -5
# Проверить доступность своего сервера
curl -v https://ntfy.yourdomain.com/health
</code></pre>
<p>На Android: Settings — Apps — ntfy — Battery — Unrestricted. Без этого Android задерживает или блокирует уведомления при заблокированном экране.</p>
<p>Проверь что имя топика в приложении точно совпадает с именем в URL запроса. Регистр важен: <code>myAlerts</code> и <code>myalerts</code> — разные топики.</p>
<h3>Telegram: бот не отвечает</h3>
<pre><code class="language-bash">
# Шаг 1: проверить что токен правильный
curl -s "https://api.telegram.org/botTOKEN/getMe"
# Должен вернуть JSON с данными бота
# Шаг 2: проверить chat_id - написать боту и смотреть updates
curl -s "https://api.telegram.org/botTOKEN/getUpdates"
# Шаг 3: тест отправки с разбором ответа
curl -s -X POST "https://api.telegram.org/botTOKEN/sendMessage" \
-d chat_id="CHAT_ID" \
-d text="test" | python3 -m json.tool
</code></pre>
<p>Ошибка <code>{"ok":false,"error_code":400,"description":"Bad Request: chat not found"}</code> — неверный chat_id. Ошибка <code>401 Unauthorized</code> — неверный токен. Ошибка <code>429 Too Many Requests</code> — превышен рейт-лимит, добавь задержку между запросами.</p>
<h3>Gotify: сообщения в веб-интерфейсе есть, на телефон не приходят</h3>
<p>У Gotify нет FCM/APNs интеграции. Уведомления на Android приходят через WebSocket — только пока приложение активно или есть постоянное соединение. Если телефон заснул — соединение рвётся, уведомление не приходит.</p>
<p>Решение: используй ntfy вместо Gotify для мобильных уведомлений, или отправляй через Apprise одновременно в Gotify и ntfy.</p>
<h3>OneSignal: delivery rate низкий</h3>
<p>Смотри в Analytics — Delivery. Если показатель Delivered низкий относительно Sent — часть пользователей заблокировала уведомления в браузере. Это не исправляется на стороне сервиса. Только контентом и частотой: меньше мусора, больше полезного, реже.</p>
<h3>FCM: ошибка InvalidRegistration</h3>
<pre><code class="language-bash">
# Ответ FCM с ошибкой:
# {"results":[{"error":"InvalidRegistration"}]}
# Токен устройства устарел или приложение удалено.
# Удали этот токен из базы и не используй его.
# FCM возвращает новый токен через onTokenRefresh в SDK.
</code></pre>
<h3>ntfy self-hosted: ошибка 401 при отправке</h3>
<pre><code class="language-bash">
# Проверить список пользователей
docker exec -it ntfy ntfy user list
# Проверить права пользователя на топик
docker exec -it ntfy ntfy access myuser alerts
# Дать права если их нет
docker exec -it ntfy ntfy access myuser alerts rw
# Сбросить пароль
docker exec -it ntfy ntfy user change-pass myuser
</code></pre>
<h2>FAQ</h2>
<h3>Что лучше — ntfy или Gotify?</h3>
<p>Для серверных алертов через curl — ntfy. Проще, работает без токенов приложений, есть официальный iOS-клиент. Gotify лучше когда нужна история сообщений с поиском и управление несколькими приложениями через один веб-интерфейс. Ключевое различие: iOS — только ntfy, у Gotify нет официального iOS-клиента и полноценной FCM/APNs интеграции.</p>
<h3>Можно ли использовать Telegram вместо push-сервиса?</h3>
<p>Для личных и командных уведомлений — да, и часто удобнее. История, поиск, пересылка, групповые чаты. Для web push на сайтах через браузер — нет. Для массовых рассылок — осторожно: 30 сообщений/сек на бота, 20 в минуту в один чат. При превышении бот получает 429 и сообщения теряются.</p>
<h3>Как получать push уведомления для Proxmox бесплатно?</h3>
<p>Proxmox 8.1 и новее: встроенная поддержка Gotify и webhook через GUI в Datacenter — Notifications. Для более старых версий — <a class="wpil_keyword_link" href="https://it-apteka.com/tag/bash/" target="_blank" rel="noopener" title="Bash" data-wpil-keyword-link="linked" data-wpil-monitor-id="2380">bash</a>-скрипт с curl в Telegram или ntfy, вызываемый из vzdump postscript. Готовое решение: Gotify + Proxmox 8.1 без единой строки кода.</p>
<h3>Почему push уведомление не работает после настройки?</h3>
<p>Три самые частые причины: неверное имя топика (регистр важен), Android убивает приложение в фоне из-за оптимизации батареи, отсутствие авторизации при включённом auth-default-access: deny-all. Проверяй в этом порядке — закрывает 90% случаев.</p>
<h3>Как проверить что push уведомления работают правильно?</h3>
<pre><code class="language-bash">
# ntfy - отправить тест и проверить доставку
curl -d "Test $(date)" https://ntfy.sh/ТВОЙ_ТОПИК
# Telegram - проверить статус бота
curl -s "https://api.telegram.org/botTOKEN/getMe"
# Gotify - health check
curl -s -H "X-Gotify-Key: TOKEN" https://GOTIFY_URL/health
</code></pre>
<h3>Какой push-сервис работает без интернета в изолированной сети?</h3>
<p>ntfy self-hosted и Gotify self-hosted. Разворачиваются в Docker, работают полностью локально. Нужен только доступ устройств к <a href="https://it-apteka.com/dhcp-snooping-chto-jeto-takoe-i-kak-zashhitit-set-ot-rogue-dhcp-servera-2/" title="DHCP Snooping — что это такое и как защитить сеть от Rogue DHCP сервера" target="_blank" rel="noopener" data-wpil-monitor-id="2385">серверу по внутренней сети</a>. Telegram, OneSignal, FCM требуют интернета — в изолированной сети не работают.</p>
<h3>Как отправить push уведомление из cron без лишних скриптов?</h3>
<pre><code class="language-bash">
# Одна строка в crontab - уведомление по результату команды
0 3 * * * /usr/local/bin/backup.sh && \
curl -s -d "Backup OK" https://ntfy.sh/myserver || \
curl -s -H "Priority: urgent" -d "Backup FAILED" https://ntfy.sh/myserver
</code></pre>
<h2>Итог</h2>
<p>Выбор очевидный если знать под что выбирать.</p>
<p>Серверный мониторинг — ntfy если нужен iOS, Gotify если история важнее мобильной доставки. Оба self-hosted, оба бесплатные, оба работают через curl без SDK и регистрации. Личные алерты — Telegram Bot API. Настраивается за 5 минут, бесплатно, история сообщений в комплекте. Сайт и web push — OneSignal, бесплатный лимит в 10 000 подписчиков закрывает потребности небольшого и среднего проекта. Мобильное приложение — FCM, альтернативы нет. Несколько каналов одновременно — Apprise, один конфиг вместо пяти интеграций.</p>
<p>Чувствительные данные — только через self-hosted. Никаких паролей и строк подключения через облачные сервисы.</p>
<p>Если что-то не заработало или выбираешь между вариантами под конкретную задачу — пиши в комментарии, разберёмся.</p>
"Официальная
<br />
ntfy — документация и self-hosted: <a href="https://docs.ntfy.sh" target="_blank" rel="noopener noreferrer">docs.ntfy.sh</a></p>
<p>Gotify — документация: <a href="https://gotify.net/docs" target="_blank" rel="noopener noreferrer">gotify.net/docs</a></p>
<p>OneSignal — web push quick start: <a href="https://documentation.onesignal.com" target="_blank" rel="noopener noreferrer">documentation.onesignal.com</a></p>
<p>Firebase Cloud Messaging: <a href="https://firebase.google.com/docs/cloud-messaging" target="_blank" rel="noopener noreferrer">firebase.google.com/docs/cloud-messaging</a></p>
<p>Telegram Bot API: <a href="https://core.telegram.org/bots/api" target="_blank" rel="noopener noreferrer">core.telegram.org/bots/api</a></p>
<p>Apprise: <a href="https://github.com/caronc/apprise/wiki" target="_blank" rel="nofollow noopener noreferrer">github.com/caronc/apprise/wiki</a><br />
Быстрый ответ
- Сервер, Docker, скрипты — ntfy (curl в одну строку, iOS + Android, self-hosted)
- Личные алерты на телефон — Telegram Bot API (бесплатно, без лимитов, 5 минут настройки)
- Сайт и web push для пользователей — OneSignal (до 10 000 подписчиков бесплатно)
- Мобильное приложение — Firebase Cloud Messaging (без лимитов на сообщения)
- Изолированная сеть без интернета — ntfy self-hosted или Gotify
Диагноз: почему «просто поднять push» — это не просто
Push уведомления бесплатно — это реально. Но перед тем как копировать первую команду из статьи, стоит понять что именно ты выбираешь и почему.
Поднять полноценную push-инфраструктуру с нуля — это несколько месяцев работы. FCM требует аккаунта разработчика Google, APNs для iOS — платного Apple Developer за $99 в год, сертификатов, масштабирования. Web Push Protocol — VAPID-ключи, Service Worker, обработка подписок. Всё это уже решено за тебя в готовых сервисах.
Что получишь из этой статьи: сравнение семи инструментов, готовые команды под каждый сценарий, примеры интеграции с Zabbix, Uptime Kuma и Proxmox, и честный разбор ограничений бесплатных тарифов. Время на настройку — от 5 минут для Telegram до 20 минут для ntfy self-hosted.
Что нужно: сервер или VPS под self-hosted варианты, Docker (опционально), телефон с Android или iOS.
Как работают push уведомления: схема за 60 секунд
Push уведомление — это сообщение, которое сервер отправляет на устройство без запроса со стороны клиента. Устройство не поллит сервер каждые N секунд. Вместо этого держит постоянное соединение с push-провайдером, который и доставляет сообщения.
На практике цепочка выглядит так:
%%{init: {
'theme': 'base',
'themeVariables': {
'primaryColor': '#ffffff',
'primaryTextColor': '#1e293b',
'primaryBorderColor': '#94a3b8',
'lineColor': '#64748b',
'fontSize': '15px',
'fontFamily': 'ui-sans-serif, system-ui, sans-serif'
},
'flowchart': {'curve': 'linear', 'nodeSpacing': 50, 'rankSpacing': 50}
}}%%
flowchart TD
A["Твой сервер или скрипт"] --> B["Push-провайдер"]
B --> C["FCM - Android"]
B --> D["APNs - iOS"]
B --> E["WebSocket - браузер"]
B --> F["Long-poll - self-hosted"]
style A fill:#f8fafc,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style B fill:#f8fafc,stroke:#f97316,stroke-width:2px,color:#9a3412
style C fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
style D fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
style E fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
style F fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
Для Android постоянное TCP-соединение держит Google через FCM. Для iOS — Apple через APNs. Для браузеров — Web Push Protocol с VAPID-ключами. Self-hosted решения типа ntfy и Gotify используют Server-Sent Events или WebSocket и держат соединение сами.
Ключевой момент: на Android и iOS ты не можешь доставить push напрямую со своего сервера на устройство. Всегда идёшь через FCM или APNs — либо напрямую, либо через промежуточный сервис. ntfy и Gotify используют FCM/APNs под капотом для своих мобильных приложений.
Сравнение бесплатных push-сервисов: таблица
| Сервис |
Self-hosted |
Android |
iOS |
Браузер |
API |
Бесплатно |
Лучший сценарий |
| ntfy |
Да |
Да |
Да |
Да |
curl, REST |
Полностью |
Серверы, скрипты, мониторинг |
| Gotify |
Да |
Да |
Нет |
Да |
REST |
Полностью |
DevOps, домашний сервер |
| OneSignal |
Нет |
Да |
Да |
Да |
REST |
До 10 000 подписчиков |
Сайты, маркетинг, web push |
| Firebase FCM |
Нет |
Да |
Да |
Да |
REST, SDK |
Без лимита сообщений |
Мобильные приложения |
| Pushover |
Нет |
Да |
Да |
Нет |
Да |
Пробный период, потом $5 разово |
Личные алерты |
| Telegram Bot API |
Нет (облако Telegram) |
Да |
Да |
Нет |
REST |
Полностью |
Личные и командные алерты |
| Apprise |
Да |
Через ntfy |
Через ntfy |
Нет |
CLI, REST |
Полностью |
Агрегатор, 80+ каналов |
Что выбрать под конкретную задачу
| Сценарий |
Что выбрать |
Почему именно это |
| Домашний сервер, Docker, Proxmox |
ntfy или Gotify |
Self-hosted, curl в одну строку, нет облака |
| Сайт и web push для аудитории |
OneSignal |
Готовый виджет подписки, аналитика, бесплатно до 10k |
| Мобильное приложение Android/iOS |
Firebase Cloud Messaging |
Канонический способ, без лимитов на сообщения |
| Личные уведомления на телефон |
Telegram Bot API |
Бесплатно, без регистрации сервисов, 5 минут |
| Zabbix, Grafana, Uptime Kuma |
ntfy или Telegram |
Встроенная интеграция, webhook, curl |
| Изолированная сеть без интернета |
ntfy self-hosted или Gotify |
Работают полностью локально |
| Нужно покрыть несколько каналов сразу |
Apprise |
Slack, Telegram, ntfy, email — всё в одной команде |
ntfy: самый простой push для серверов
Что это и почему именно ntfy
ntfy — open-source push-сервис, который работает по принципу pub/sub через HTTP. Отправляешь POST-запрос на URL с именем топика — уведомление приходит на все подписанные устройства. Никакой регистрации. Никаких токенов приложений. Никакого SDK.
Есть публичный сервер ntfy.sh — бесплатно, но топики публичны по умолчанию. Для приватных алертов — разворачивай свой экземпляр. На это уходит 3 минуты с Docker.
Почему ntfy, а не Gotify: iOS-клиент. У Gotify официального iOS-приложения нет. Если в команде есть хоть один iPhone — выбор за ntfy.
Системные требования
| Компонент |
Минимум |
Рекомендуется |
| ОС |
Linux (любой дистрибутив), Windows (WSL2) |
Ubuntu 22.04 LTS / Debian 12 |
| Docker |
20.10+ |
24.x+ |
| RAM |
64 MB |
256 MB |
| Диск |
100 MB |
1 GB (под кэш сообщений) |
| ntfy |
v2.x |
Актуальная версия — проверяй на GitHub Releases |
На момент публикации актуальна версия ntfy v2.11.0. Перед установкой проверь свежие релизы на GitHub.
Установка ntfy через Docker Compose
Создай директории и файл конфига:
mkdir -p /etc/ntfy /var/cache/ntfy
Файл /etc/ntfy/server.yml:
base-url: https://ntfy.yourdomain.com
auth-file: /var/cache/ntfy/auth.db
auth-default-access: deny-all
behind-proxy: true
cache-file: /var/cache/ntfy/cache.db
cache-duration: 24h
Docker Compose файл docker-compose.yml:
services:
ntfy:
image: binwiederhier/ntfy
command: serve
ports:
- "8080:80"
volumes:
- /etc/ntfy:/etc/ntfy
- /var/cache/ntfy:/var/cache/ntfy
environment:
- TZ=Europe/Moscow
restart: unless-stopped
docker compose up -d
# Создать пользователя
docker exec -it ntfy ntfy user add --role=user myuser
# Дать права на топик
docker exec -it ntfy ntfy access myuser alerts rw
# Проверить что сервер живой
curl -s http://localhost:8080/health
# Должен вернуть: {"healthy":true}
Таблица портов ntfy
| Порт |
Протокол |
Назначение |
Снаружи |
| 8080 |
TCP |
HTTP API, веб-интерфейс |
Через reverse proxy (443) |
| 443 |
TCP |
HTTPS через Nginx/Caddy |
Да |
Отправка уведомления через curl
# Простое уведомление на публичный сервер
curl -d "Backup completed" https://ntfy.sh/myserver-alerts
# С заголовком, приоритетом и эмодзи-тегом
curl -H "Title: Backup Done" \
-H "Priority: high" \
-H "Tags: white_check_mark" \
-d "Backup of db01 completed successfully" \
https://ntfy.sh/myserver-alerts
# На свой сервер с авторизацией
curl -u myuser:mypassword \
-d "Disk usage > 90% on node01" \
https://ntfy.yourdomain.com/alerts
Приоритеты: min, low, default, high, urgent. При urgent — телефон вибрирует даже в режиме «Не беспокоить».
Отправка из PowerShell
# Уведомление из Windows-скрипта
Invoke-RestMethod -Uri "https://ntfy.sh/myserver-alerts" `
-Method Post `
-Body "Windows Update completed on WS2022-01"
# С заголовком и приоритетом
Invoke-RestMethod -Uri "https://ntfy.sh/myserver-alerts" `
-Method Post `
-Headers @{
"Title" = "Update Done"
"Priority" = "high"
} `
-Body "Patch Tuesday done, reboot scheduled"
Nginx reverse proxy для ntfy
server {
listen 443 ssl;
server_name ntfy.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/ntfy.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ntfy.yourdomain.com/privkey.pem;
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;
# Обязательно для SSE - иначе уведомления будут приходить с задержкой
proxy_buffering off;
proxy_read_timeout 3600s;
}
}
Интеграция ntfy с Uptime Kuma
В Uptime Kuma перейди в Settings — Notifications — Add Notification. Выбери тип ntfy. Укажи:
- Server URL:
https://ntfy.sh или твой сервер
- Topic: имя твоего топика
- Priority: 4 для обычных, 5 для критических
Uptime Kuma отправляет уведомления при падении сервиса и при восстановлении. Работает из коробки, без скриптов.
Подписка на уведомления
Android: ntfy из Google Play. iOS: ntfy из App Store. Браузер: зайди на ntfy.sh или свой сервер и подпишись на топик через веб-интерфейс. Укажи имя топика — всё, подписан.
Gotify: self-hosted с историей и веб-интерфейсом
Чем Gotify отличается от ntfy
Gotify — self-hosted push-сервер с веб-интерфейсом, REST API и WebSocket. Требует создания приложения и токена — чуть больше настройки. Зато история сообщений хранится без ограничений по времени, есть поиск, управление несколькими приложениями через один сервер.
iOS официально не поддерживается — нет APNs-интеграции. Если нужен iPhone — используй ntfy. Если все на Android или только браузер — Gotify подходит отлично.
Установка через Docker Compose
services:
gotify:
image: gotify/server
ports:
- "8080:80"
volumes:
- /var/gotify/data:/app/data
environment:
- GOTIFY_DEFAULTUSER_PASS=changeme
restart: unless-stopped
docker compose up -d
# Открой http://localhost:8080
# Войди: admin / changeme - сразу смени пароль
# Создай приложение: Apps - Create Application
# Скопируй токен - он понадобится для отправки
Отправка уведомления через curl
# Отправить сообщение через токен приложения
curl -X POST "https://gotify.yourdomain.com/message" \
-H "X-Gotify-Key: YOUR_APP_TOKEN" \
-H "Content-Type: application/json" \
-d '{"title":"Backup Done","message":"db01 backup completed","priority":5}'
# Из bash-скрипта с переменными
GOTIFY_URL="https://gotify.yourdomain.com"
GOTIFY_TOKEN="YOUR_APP_TOKEN"
MESSAGE="Disk usage on node01: 87%"
curl -s -X POST "$GOTIFY_URL/message" \
-H "X-Gotify-Key: $GOTIFY_TOKEN" \
-F "title=Disk Alert" \
-F "message=$MESSAGE" \
-F "priority=7"
Priority в Gotify: 1-3 низкий, 4-6 средний, 7-9 высокий, 10 критический. Высокий приоритет — телефон не игнорирует уведомление даже при режиме тишины.
Интеграция Gotify с Zabbix
В Zabbix настрой Media Type — Webhook. URL: https://gotify.yourdomain.com/message. Метод: POST. Заголовок: X-Gotify-Key: {TOKEN}. Тело:
{
"title": "{TRIGGER.NAME}",
"message": "Host: {HOST.NAME}\nStatus: {TRIGGER.STATUS}\nSeverity: {TRIGGER.SEVERITY}",
"priority": 7
}
Gotify + Proxmox 8.1: поддержка встроена нативно. Идёшь в Datacenter — Notifications — Add — Gotify. Указываешь URL и токен — всё. Никаких скриптов.
Telegram Bot API: быстрее всего для личных алертов
Почему Telegram часто удобнее специализированного push-сервиса
Telegram Bot API — бесплатный, без регистрации сервисов, без лимитов для личных ботов. Уведомление приходит как обычное сообщение — история сохраняется, поиск работает, можно переслать коллеге.
Для командных алертов особенно удобно: создаёшь группу, добавляешь бота — все в команде видят алерты в одном месте. Никакого отдельного мобильного приложения. Никакой дополнительной настройки у каждого сотрудника.
Минус один: нет web push. Для уведомлений на сайте через браузер Telegram не подходит.
Создание бота: 5 минут
- Напиши
@BotFather в Telegram
- Отправь
/newbot, придумай имя и username
- Получи токен вида
123456789:AAF_xxxxxxxx — сохрани его
- Напиши боту любое сообщение чтобы начать чат
- Узнай свой chat_id — открой в браузере:
https://api.telegram.org/botТВОЙ_ТОКЕН/getUpdates
- Найди поле
"chat":{"id":... — это и есть твой chat_id
Отправка через curl
# Простое уведомление
curl -s -X POST "https://api.telegram.org/bot123456789:AAF_xxx/sendMessage" \
-d chat_id="YOUR_CHAT_ID" \
-d text="Proxmox backup failed on pve01"
# С Markdown форматированием
curl -s -X POST "https://api.telegram.org/botTOKEN/sendMessage" \
-d chat_id="CHAT_ID" \
-d parse_mode="Markdown" \
-d text="*ALERT* - Disk usage on _node01_ is *92%*"
# Функция для переиспользования в скриптах
send_telegram() {
local message="$1"
curl -s -X POST "https://api.telegram.org/botTOKEN/sendMessage" \
-d chat_id="CHAT_ID" \
-d text="$message" > /dev/null
}
# Пример использования
send_telegram "Backup completed: $(date)"
Из PowerShell
$Token = "123456789:AAF_xxxxxxxxxxxxxxxx"
$ChatId = "YOUR_CHAT_ID"
$Message = "Windows backup completed on $(hostname)"
Invoke-RestMethod -Uri "https://api.telegram.org/bot$Token/sendMessage" `
-Method Post `
-Body @{
chat_id = $ChatId
text = $Message
}
Telegram в Proxmox (bash-скрипт)
# /usr/local/bin/notify-telegram.sh
#!/bin/bash
TOKEN="YOUR_BOT_TOKEN"
CHAT_ID="YOUR_CHAT_ID"
MESSAGE="$1"
curl -s -X POST "https://api.telegram.org/bot$TOKEN/sendMessage" \
-d chat_id="$CHAT_ID" \
-d text="[Proxmox] $MESSAGE"
chmod +x /usr/local/bin/notify-telegram.sh
# Проверка
/usr/local/bin/notify-telegram.sh "Test alert from pve01"
Для vzdump: в конфиге /etc/vzdump.conf добавь строку script: /usr/local/bin/notify-telegram.sh. После каждого бэкапа скрипт получит статус как аргумент.
OneSignal: web push для сайтов и маркетинга
Что OneSignal умеет бесплатно
На бесплатном тарифе OneSignal даёт: до 10 000 активных подписчиков, неограниченное количество уведомлений, сегментацию аудитории, A/B-тестирование, аналитику доставки и кликов. Это реальные возможности без платы — не урезанная демо-версия.
Брендинг OneSignal присутствует в prompt подписки, но не в самих уведомлениях. Пользователь видит уведомление от твоего сайта.
Интеграция для сайта
- Зарегистрируйся на onesignal.com
- Создай приложение — выбери Web Push
- Укажи домен сайта
- Скопируй App ID
<!-- Вставь в <head> -->
<script src="https://cdn.onesignal.com/sdks/web/v16/OneSignalSDK.page.js" defer></script>
<script>
window.OneSignalDeferred = window.OneSignalDeferred || [];
OneSignalDeferred.push(async function(OneSignal) {
await OneSignal.init({
appId: "YOUR_APP_ID",
});
});
</script>
После вставки браузер покажет системный prompt на разрешение уведомлений. Пользователи, которые согласятся, начнут получать push.
Отправка через OneSignal REST API
# Отправить всем подписчикам
curl -X POST "https://onesignal.com/api/v1/notifications" \
-H "Content-Type: application/json" \
-H "Authorization: Basic YOUR_REST_API_KEY" \
-d '{
"app_id": "YOUR_APP_ID",
"included_segments": ["All"],
"contents": {"en": "Новая статья опубликована"},
"headings": {"en": "IT-Apteka.com"}
}'
# Только подписчикам с тегом plan:pro
curl -X POST "https://onesignal.com/api/v1/notifications" \
-H "Content-Type: application/json" \
-H "Authorization: Basic YOUR_REST_API_KEY" \
-d '{
"app_id": "YOUR_APP_ID",
"filters": [{"field": "tag", "key": "plan", "relation": "=", "value": "pro"}],
"contents": {"en": "Обновление для PRO доступно"},
"headings": {"en": "Обновление"}
}'
Firebase Cloud Messaging: для мобильных приложений
Когда FCM, а не ntfy
Firebase Cloud Messaging — канонический способ доставки push на Android. Если пишешь мобильное приложение — идёшь через FCM. Для серверных алертов и скриптов FCM избыточен: требует проекта в Firebase, Service Account, JSON-ключей. ntfy или Telegram проще в два раза.
Лимиты по сообщениям фактически отсутствуют — FCM бесплатен без явных ограничений на количество устройств и сообщений.
Отправка через FCM HTTP API v1
# Получить access token через Service Account
ACCESS_TOKEN=$(gcloud auth print-access-token)
# Отправить уведомление на конкретное устройство
curl -X POST \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"message": {
"token": "DEVICE_FCM_TOKEN",
"notification": {
"title": "Сервер упал",
"body": "Проверь nginx на node01"
}
}
}' \
"https://fcm.googleapis.com/v1/projects/YOUR_PROJECT_ID/messages:send"
Legacy FCM API устаревает
Google объявил об отключении Legacy HTTP API. Если пишешь новую интеграцию — используй FCM HTTP API v1 сразу. Legacy работает, но дата отключения уже перенесена несколько раз — когда-нибудь выключат без предупреждения.
Apprise: один агрегатор для всех каналов
Зачем нужен агрегатор
Apprise — open-source библиотека и CLI, который подключает более 80 сервисов уведомлений через единый интерфейс. Telegram, ntfy, Gotify, Slack, Discord, email, Matrix, PagerDuty — всё через одну команду или строку конфига.
Полезно когда: нужно отправлять алерт одновременно в Telegram и на email, или переключаться между провайдерами без правки скриптов.
# Установка
pip install apprise
# Уведомление в Telegram
apprise -b "Backup completed" "tgram://BOT_TOKEN/CHAT_ID"
# Одновременно в Telegram и ntfy
apprise -b "Server alert" \
"tgram://BOT_TOKEN/CHAT_ID" \
"ntfy://ntfy.sh/myserver"
# Из конфига
apprise -b "Backup done" --config /etc/apprise.yml
Файл /etc/apprise.yml:
urls:
- tgram://BOT_TOKEN/CHAT_ID
- ntfy://ntfy.sh/myserver-alerts
- gotify://gotify.yourdomain.com/APP_TOKEN
Apprise встроен в Uptime Kuma, Homer и Healthchecks — если уже используешь эти инструменты, Apprise уже доступен без отдельной установки.
Практические примеры для серверного мониторинга
Пример 1: уведомление из bash-скрипта при завершении бэкапа
#!/bin/bash
# /usr/local/bin/backup-db.sh
BACKUP_DIR="/var/backups/postgres"
DATE=$(date +%Y%m%d_%H%M)
BACKUP_FILE="$BACKUP_DIR/pg_dump_$DATE.sql.gz"
pg_dump mydb | gzip > "$BACKUP_FILE"
if [ $? -eq 0 ]; then
SIZE=$(du -sh "$BACKUP_FILE" | cut -f1)
curl -s \
-H "Title: Backup OK" \
-H "Tags: white_check_mark" \
-d "pg_dump OK: $BACKUP_FILE ($SIZE)" \
https://ntfy.sh/myserver-alerts
else
curl -s \
-H "Title: BACKUP FAILED" \
-H "Priority: urgent" \
-H "Tags: rotating_light" \
-d "pg_dump returned error on $(hostname) at $(date)" \
https://ntfy.sh/myserver-alerts
fi
Пример 2: мониторинг диска с алертом в Telegram
#!/bin/bash
# Запускать через cron: */15 * * * * /usr/local/bin/disk-check.sh
THRESHOLD=85
TELEGRAM_TOKEN="YOUR_TOKEN"
TELEGRAM_CHAT="YOUR_CHAT_ID"
df -h | grep -vE '^Filesystem|tmpfs|cdrom' | awk '{ print $5 " " $1 }' | while read usage mount; do
PERCENT=$(echo $usage | sed 's/%//')
if [ "$PERCENT" -ge "$THRESHOLD" ]; then
MSG="DISK ALERT: $mount is ${PERCENT}% full on $(hostname)"
curl -s -X POST "https://api.telegram.org/bot$TELEGRAM_TOKEN/sendMessage" \
-d chat_id="$TELEGRAM_CHAT" \
-d text="$MSG"
fi
done
Пример 3: функция Send-NtfyAlert в PowerShell
function Send-NtfyAlert {
param(
[string]$Message,
[string]$Title = "Server Alert",
[string]$Priority = "default",
[string]$Topic = "myserver-alerts"
)
Invoke-RestMethod -Uri "https://ntfy.sh/$Topic" `
-Method Post `
-Headers @{
"Title" = $Title
"Priority" = $Priority
} `
-Body $Message
}
# Использование в скрипте обслуживания
try {
Start-Process "robocopy" -ArgumentList "C:\Data \\backup\Data /MIR" -Wait
Send-NtfyAlert -Message "Robocopy completed on $(hostname)" -Title "Backup Done"
} catch {
Send-NtfyAlert -Message "Backup FAILED: $($_.Exception.Message)" `
-Title "Backup Error" `
-Priority "urgent"
}
Пример 4: Uptime Kuma + Gotify
В Uptime Kuma: Settings — Notifications — Add Notification — выбери Gotify. Укажи:
- Server URL:
https://gotify.yourdomain.com
- Application Token: токен из Gotify веб-интерфейса
- Priority: 7 для критических алертов
Gotify хранит полную историю уведомлений с временными метками. Можно открыть веб-интерфейс и посмотреть хронологию инцидентов — что упало, когда восстановилось.
Пример 5: уведомление из cron одной строкой
# В crontab - уведомление при завершении задачи
0 3 * * * /usr/local/bin/backup.sh && \
curl -s -d "Backup OK $(date)" https://ntfy.sh/myserver || \
curl -s -H "Priority: urgent" -d "Backup FAILED $(date)" https://ntfy.sh/myserver
Ограничения бесплатных тарифов: что реально режет
| Сервис |
Лимит подписчиков |
Лимит сообщений |
История |
SLA |
White-label |
| ntfy.sh (публичный) |
Нет |
250/день на IP |
12 часов |
Нет |
Self-hosted |
| Gotify (self-hosted) |
Нет |
Нет |
Диск |
Твой сервер |
Да |
| OneSignal |
10 000 |
Нет |
30 дней |
Нет |
Нет |
| Firebase FCM |
Нет |
Rate limit по скорости |
Нет |
Нет |
Нет |
| Pushover |
Нет |
10 000/мес на trial |
7 дней |
Нет |
Нет |
| Telegram Bot API |
Нет |
30 сообщ/сек на бота |
В Telegram |
Нет |
Нет |
Что реально бьёт по рукам:
Нет SLA ни у одного бесплатного облачного сервиса. Для критических алертов это проблема. Если ntfy.sh лежит — ты не знаешь что твой сервер упал. Для критического мониторинга — только self-hosted или резервный канал (например, Telegram как дублирующий канал к ntfy).
Короткая история на ntfy.sh. 12 часов. Ночные события можешь не увидеть утром. Self-hosted ntfy или Gotify хранят столько сколько хватает диска.
Рейт-лимиты Telegram. 30 сообщений/сек на бота, 20 сообщений/мин в один чат. При массовых рассылках из мониторинга — бот начнёт получать 429 Too Many Requests. Решение: батчинг алертов или дедупликация на стороне отправителя.
Лимит подписчиков OneSignal. 10 000 — это много для небольшого сайта. Но при росте платный тариф начинается от $9/месяц. Планируй заранее если проект масштабируется.
Безопасность push уведомлений
Push уведомления часто содержат чувствительные данные. IP-адреса серверов. Имена хостов. Тексты ошибок с путями к файлам. Всё это летит через серверы третьих сторон.
Что нельзя отправлять через облачные push-сервисы
Пароли, токены, ключи API — никогда. Персональные данные пользователей — нарушение GDPR. Полные трейсбэки с внутренними путями и именами переменных — помогает атакующему. Строки подключения к базам данных — особенно.
Чувствительные алерты — только self-hosted. ntfy или Gotify на своём сервере. Данные не покидают инфраструктуру.
Только HTTPS для своего push-сервера. Nginx как reverse proxy с сертификатом Let’s Encrypt. Без HTTPS — уведомления читаемы в сети.
Авторизация обязательна. Публичные топики ntfy.sh доступны всем. На своём сервере включай auth-default-access: deny-all.
Не публикуй endpoint в открытом репозитории. URL вида ntfy.sh/company-prod-alerts в публичном GitHub — это подарок любому желающему флудить в твой топик.
Ограничивай IP для токенов. В Gotify и в Telegram bot webhook настраивай allowlist IP откуда принимаются запросы.
Схема безопасной self-hosted архитектуры
%%{init: {
'theme': 'base',
'themeVariables': {
'primaryColor': '#ffffff',
'primaryTextColor': '#1e293b',
'primaryBorderColor': '#94a3b8',
'lineColor': '#64748b',
'fontSize': '15px',
'fontFamily': 'ui-sans-serif, system-ui, sans-serif'
},
'flowchart': {'curve': 'linear', 'nodeSpacing': 50, 'rankSpacing': 50}
}}%%
flowchart TD
A["Скрипт или мониторинг"] --> B["Nginx HTTPS + auth"]
B --> C["ntfy self-hosted"]
C --> D["FCM - Google"]
C --> E["APNs - Apple"]
D --> F["Android"]
E --> G["iOS"]
H["fail2ban"] --> B
style A fill:#f8fafc,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style B fill:#f8fafc,stroke:#f97316,stroke-width:2px,color:#9a3412
style C fill:#f8fafc,stroke:#3b82f6,stroke-width:2px,color:#1e40af
style D fill:#f8fafc,stroke:#94a3b8,stroke-width:2px,color:#475569
style E fill:#f8fafc,stroke:#94a3b8,stroke-width:2px,color:#475569
style F fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
style G fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
style H fill:#f8fafc,stroke:#ef4444,stroke-width:2px,color:#dc2626
Почему web push раздражает пользователей и как не стать частью проблемы
Web push — технически правильная вещь. Сайт может уведомить пользователя о новой статье или важном событии без email. Но в руках маркетологов это превратилось в помойку — 95% пользователей кликают «Заблокировать» при виде prompt подписки.
Как убивают доверие к web push: показывают prompt при первом посещении без контекста, отправляют 5-10 уведомлений в день, используют кликбейт в заголовках, имитируют системные предупреждения.
Как делать правильно: запрашивай разрешение после конкретного действия пользователя — регистрации, заказа, нескольких прочитанных статей. Объясняй что именно он получит. Давай простой способ отписаться. Не отправляй больше 1-2 уведомлений в день. Delivery rate 50-60% при таком подходе против 5-10% при агрессивных промптах. Одно уведомление которое открыли всегда лучше десяти которые заблокировали.
Резервное копирование и профилактика
Self-hosted push-сервер — это инфраструктура. Относись к нему как к инфраструктуре.
Что бэкапить: для ntfy — /var/cache/ntfy/cache.db и /var/cache/ntfy/auth.db, конфиг /etc/ntfy/server.yml. Для Gotify — директорию /var/gotify/data целиком.
Как часто: ежедневно для auth.db (пользователи и права), еженедельно для cache.db (история сообщений). Конфиг — в git-репозиторий, изменения после каждой правки.
Как проверить работу:
# Проверка ntfy
curl -s http://localhost:8080/health
# Ожидаем: {"healthy":true}
# Тестовое уведомление
curl -d "Health check $(date)" https://ntfy.yourdomain.com/monitoring
# Проверка Gotify
curl -s -H "X-Gotify-Key: TOKEN" https://gotify.yourdomain.com/health
Автозапуск: оба сервиса запускаются через Docker с restart: unless-stopped — это уже покрывает перезагрузку хоста. Docker daemon должен быть включён в systemd:
systemctl enable docker
systemctl status docker
Мониторинг самого push-сервера: добавь URL health endpoint в Uptime Kuma. Получится ситуация когда Uptime Kuma мониторит ntfy, а ntfy отправляет алерты от Uptime Kuma. Классика.
Troubleshooting
ntfy: сообщения не приходят на телефон
Ошибка: уведомления отправляются, но телефон молчит
Сначала проверь
настройки батареи на Android. Android агрессивно убивает фоновые процессы — ntfy может не получать push при заблокированном экране.
# Проверить что сообщение дошло до сервера
curl -s "https://ntfy.sh/myserver-alerts/json?poll=1" | head -5
# Проверить доступность своего сервера
curl -v https://ntfy.yourdomain.com/health
На Android: Settings — Apps — ntfy — Battery — Unrestricted. Без этого Android задерживает или блокирует уведомления при заблокированном экране.
Проверь что имя топика в приложении точно совпадает с именем в URL запроса. Регистр важен: myAlerts и myalerts — разные топики.
Telegram: бот не отвечает
# Шаг 1: проверить что токен правильный
curl -s "https://api.telegram.org/botTOKEN/getMe"
# Должен вернуть JSON с данными бота
# Шаг 2: проверить chat_id - написать боту и смотреть updates
curl -s "https://api.telegram.org/botTOKEN/getUpdates"
# Шаг 3: тест отправки с разбором ответа
curl -s -X POST "https://api.telegram.org/botTOKEN/sendMessage" \
-d chat_id="CHAT_ID" \
-d text="test" | python3 -m json.tool
Ошибка {"ok":false,"error_code":400,"description":"Bad Request: chat not found"} — неверный chat_id. Ошибка 401 Unauthorized — неверный токен. Ошибка 429 Too Many Requests — превышен рейт-лимит, добавь задержку между запросами.
Gotify: сообщения в веб-интерфейсе есть, на телефон не приходят
У Gotify нет FCM/APNs интеграции. Уведомления на Android приходят через WebSocket — только пока приложение активно или есть постоянное соединение. Если телефон заснул — соединение рвётся, уведомление не приходит.
Решение: используй ntfy вместо Gotify для мобильных уведомлений, или отправляй через Apprise одновременно в Gotify и ntfy.
OneSignal: delivery rate низкий
Смотри в Analytics — Delivery. Если показатель Delivered низкий относительно Sent — часть пользователей заблокировала уведомления в браузере. Это не исправляется на стороне сервиса. Только контентом и частотой: меньше мусора, больше полезного, реже.
FCM: ошибка InvalidRegistration
# Ответ FCM с ошибкой:
# {"results":[{"error":"InvalidRegistration"}]}
# Токен устройства устарел или приложение удалено.
# Удали этот токен из базы и не используй его.
# FCM возвращает новый токен через onTokenRefresh в SDK.
ntfy self-hosted: ошибка 401 при отправке
# Проверить список пользователей
docker exec -it ntfy ntfy user list
# Проверить права пользователя на топик
docker exec -it ntfy ntfy access myuser alerts
# Дать права если их нет
docker exec -it ntfy ntfy access myuser alerts rw
# Сбросить пароль
docker exec -it ntfy ntfy user change-pass myuser
FAQ
Что лучше — ntfy или Gotify?
Для серверных алертов через curl — ntfy. Проще, работает без токенов приложений, есть официальный iOS-клиент. Gotify лучше когда нужна история сообщений с поиском и управление несколькими приложениями через один веб-интерфейс. Ключевое различие: iOS — только ntfy, у Gotify нет официального iOS-клиента и полноценной FCM/APNs интеграции.
Можно ли использовать Telegram вместо push-сервиса?
Для личных и командных уведомлений — да, и часто удобнее. История, поиск, пересылка, групповые чаты. Для web push на сайтах через браузер — нет. Для массовых рассылок — осторожно: 30 сообщений/сек на бота, 20 в минуту в один чат. При превышении бот получает 429 и сообщения теряются.
Как получать push уведомления для Proxmox бесплатно?
Proxmox 8.1 и новее: встроенная поддержка Gotify и webhook через GUI в Datacenter — Notifications. Для более старых версий — bash-скрипт с curl в Telegram или ntfy, вызываемый из vzdump postscript. Готовое решение: Gotify + Proxmox 8.1 без единой строки кода.
Почему push уведомление не работает после настройки?
Три самые частые причины: неверное имя топика (регистр важен), Android убивает приложение в фоне из-за оптимизации батареи, отсутствие авторизации при включённом auth-default-access: deny-all. Проверяй в этом порядке — закрывает 90% случаев.
Как проверить что push уведомления работают правильно?
# ntfy - отправить тест и проверить доставку
curl -d "Test $(date)" https://ntfy.sh/ТВОЙ_ТОПИК
# Telegram - проверить статус бота
curl -s "https://api.telegram.org/botTOKEN/getMe"
# Gotify - health check
curl -s -H "X-Gotify-Key: TOKEN" https://GOTIFY_URL/health
Какой push-сервис работает без интернета в изолированной сети?
ntfy self-hosted и Gotify self-hosted. Разворачиваются в Docker, работают полностью локально. Нужен только доступ устройств к серверу по внутренней сети. Telegram, OneSignal, FCM требуют интернета — в изолированной сети не работают.
Как отправить push уведомление из cron без лишних скриптов?
# Одна строка в crontab - уведомление по результату команды
0 3 * * * /usr/local/bin/backup.sh && \
curl -s -d "Backup OK" https://ntfy.sh/myserver || \
curl -s -H "Priority: urgent" -d "Backup FAILED" https://ntfy.sh/myserver
Итог
Выбор очевидный если знать под что выбирать.
Серверный мониторинг — ntfy если нужен iOS, Gotify если история важнее мобильной доставки. Оба self-hosted, оба бесплатные, оба работают через curl без SDK и регистрации. Личные алерты — Telegram Bot API. Настраивается за 5 минут, бесплатно, история сообщений в комплекте. Сайт и web push — OneSignal, бесплатный лимит в 10 000 подписчиков закрывает потребности небольшого и среднего проекта. Мобильное приложение — FCM, альтернативы нет. Несколько каналов одновременно — Apprise, один конфиг вместо пяти интеграций.
Чувствительные данные — только через self-hosted. Никаких паролей и строк подключения через облачные сервисы.
Если что-то не заработало или выбираешь между вариантами под конкретную задачу — пиши в комментарии, разберёмся.