ntfy — простой сервер push-уведомлений: curl на URL — и пуш на телефон. Подходит для бэкапов, Fail2ban, Uptime Kuma, Zabbix и cron-задач.
mkdir -p ~/ntfy/{cache,etc}
cd ~/ntfy
cat > etc/server.yml << 'EOF'
base-url: "https://ntfy.yourdomain"
cache-file: "/var/cache/ntfy/cache.db"
attachment-cache-dir: "/var/cache/ntfy/attachments"
auth-file: "/var/lib/ntfy/user.db"
auth-default-access: "deny-all"
behind-proxy: true
upstream-base-url: "https://ntfy.sh"
EOF
deny-all — без анонимной публикации; пользователей создадите после старта.
docker-compose.ymlservices:
ntfy:
image: binwiederhier/ntfy:latest
container_name: ntfy
restart: unless-stopped
command: serve
environment:
TZ: Europe/Moscow
volumes:
- ./cache:/var/cache/ntfy
- ./etc:/etc/ntfy
- ntfy_lib:/var/lib/ntfy
labels:
- traefik.enable=true
- traefik.http.middlewares.ntfy_https.redirectscheme.scheme=https
- traefik.http.routers.ntfy.entrypoints=web
- traefik.http.routers.ntfy.middlewares=ntfy_https@docker
- traefik.http.routers.ntfy.rule=Host(`ntfy.yourdomain`)
- traefik.http.routers.ntfy_https.entrypoints=websecure
- traefik.http.routers.ntfy_https.rule=Host(`ntfy.yourdomain`)
- traefik.http.routers.ntfy_https.tls=true
- traefik.http.routers.ntfy_https.tls.certresolver=le
- traefik.http.services.ntfy.loadbalancer.server.port=80
networks:
- traefik_network
volumes:
ntfy_lib:
networks:
traefik_network:
external: true
docker compose up -d
docker exec -it ntfy ntfy user add --role=admin youruser
Установите приложение ntfy, подпишитесь на топик, например homelab-alerts (с логином).
В веб-интерфейсе управления пользователями нет. Обычных людей и доступ к топику задаёте в CLI:
docker exec ntfy ntfy user list
docker exec -it ntfy ntfy user add username
docker exec ntfy ntfy access username homelab-alerts read-write
docker exec ntfy ntfy user del username
read-only вместо read-write — только получать пуши. Админ видит все топики; обычный пользователь — только те, куда выдали access.
Топик как сущность в базе не создаётся: достаточно имени и ACL. Новый канал тому же человеку:
docker exec ntfy ntfy access username backups read-write
docker exec ntfy ntfy access
После этого он подписывается в приложении на backups. Закрыть топик: ntfy access username backups deny.
curl -u 'youruser:password' \
-H "Title: Backup OK" \
-d "Ночной rclone завершился успешно" \
https://ntfy.yourdomain/homelab-alerts
Цепочка: триггер → Trigger action → media пользователя → ntfy.sh → топик. Кнопка Test у media type эту цепочку не проверяет.
Не кладите пароль админа в Zabbix. Служебный пользователь только на запись и токен:
docker exec -it ntfy ntfy user add zabbix
docker exec ntfy ntfy access zabbix homelab-alerts write-only
docker exec ntfy ntfy token add --label=zabbix-media zabbix
token add печатает tk_…. Людям, которые должны видеть алерты:
docker exec ntfy ntfy access username homelab-alerts read-write
В приложении: сервер https://ntfy.yourdomain, свой логин, подписка на тот же топик. Android — Instant delivery. iPhone — в server.yml строка upstream-base-url: "https://ntfy.sh", иначе фоновые пуши почти не приходят.
Сервер Zabbix должен открывать https://ntfy.yourdomain (проверка: curl -I https://ntfy.yourdomain с хоста Zabbix).
YAML-webhook в 5.0 не импортируется, нужен media type Script. Путь из AlertScriptsPath (часто /usr/lib/zabbix/alertscripts), файл ntfy.sh:
#!/bin/bash
# $1 топик $2 тема $3 текст
set -eu
topic="${1:?}"
title="${2:-Zabbix}"
body="${3:-}"
curl -fsS \
-H "Authorization: Bearer TOKEN" \
-H "Title: ${title}" \
-H "Tags: zabbix" \
-H "Priority: high" \
-d "${body}" \
"https://ntfy.yourdomain/${topic}"
chmod 755 /usr/lib/zabbix/alertscripts/ntfy.sh
chown zabbix:zabbix /usr/lib/zabbix/alertscripts/ntfy.sh
sudo -u zabbix /usr/lib/zabbix/alertscripts/ntfy.sh homelab-alerts "Test" "Проверка из Zabbix"
Если JSON с id вернулся — сеть, токен и топик живые.
Administration → Media types → Create
ntfy, Type: Script, Script name: ntfy.sh (без пути){ALERT.SENDTO}, {ALERT.SUBJECT}, {ALERT.MESSAGE}Administration → Users — у каждого, кто должен получать пуш, вкладка Media → Add:
ntfyhomelab-alerts), не email1-7,00:00-24:00Пользователь Enabled. Нет строки Media — в журнале будет No media defined for user, скрипт не вызовется.
Реальный триггер без шаблона падает с No message defined for media type. Нужен один из двух вариантов.
A. У media type ntfy вкладка Message templates: Problem, Problem recovery, Problem update (можно скопировать с Email/Telegram).
B. В операции action галка Custom message. Отдельно для проблемы и для восстановления — не один текст на обе.
Проблема (тема, затем тело):
Проблема на {HOST.NAME}: {EVENT.NAME}
Проблема началась в {EVENT.TIME} {EVENT.DATE}
Название проблемы: {EVENT.NAME}
Хост: {HOST.NAME}
Серьёзность: {TRIGGER.SEVERITY}
Исходный ID проблемы: {EVENT.ID}
{TRIGGER.URL}
Восстановление:
Решена проблема на {HOST.NAME}: {EVENT.NAME}
Проблема решена в {EVENT.RECOVERY.TIME} {EVENT.RECOVERY.DATE}
Название проблемы: {EVENT.NAME}
Длительность: {EVENT.DURATION}
Хост: {HOST.NAME}
Серьёзность: {TRIGGER.SEVERITY}
Исходный ID проблемы: {EVENT.ID}
{TRIGGER.URL}
В операции проблемы — только {EVENT.TIME} / {EVENT.DATE}. Макросы {EVENT.RECOVERY.*} там не раскрываются и уйдут в пуш как текст.
Configuration → Actions → Trigger actions (не Internal). Действие Enabled, после правок — Update.
Вкладка Operations:
1–1, длительность шага 0 (первый шаг и так сразу; час в Default step duration — пауза до шага 2)То же в Recovery operations, иначе придёт только «упало».
Галка «приостанавливать операции для подавленных проблем»: хост в maintenance — пуша не будет.
Уже висящие PROBLEM повторно не шлются. Нужна новая авария.
Импорт ntfy.yaml в Alerts → Media types, URL своего сервера и Token, у пользователя Send to — топик. Дальше те же action и подписка в приложении.
Reports → Action log после нового PROBLEM:
| В журнале | Что не так |
|---|---|
| нет строк | action не сработал: выключен, не те условия, maintenance, не Trigger actions |
No message defined for media type |
нет шаблонов / Custom message |
No media defined for user |
у пользователя нет media ntfy |
ошибка ntfy.sh / curl |
токен, URL, права на скрипт, DNS с хоста Zabbix |
| Max/Email sent, ntfy нет | в операции Send only via не ntfy или у пользователя нет этой media |
Test media type с темой «Тестовая тема» — не алерт. Настоящий пуш: тема вида Проблема на hostname: ….
auth-default-access: write-only на публичном сервере без необходимости.ntfy_lib и каталог ./etc.Итог: лёгкие push-алерты для всего домашнего сервера через HTTPS.