git-subtree-dir: .tasks-imported git-subtree-mainline:c40418239egit-subtree-split:cbdc2b39aa
6.6 KiB
6.6 KiB
vds-ntfy-push
Goal
Self-host ntfy.sh на VDS для push-нотификаций на телефон. Docker-image binwiederhier/ntfy. Использовать для:
- vds-backup-rsync-kreknin backup status (вместо/параллельно email на vitya.kuznetsov@gmail.com)
- Monitoring alerts (CMS down, traefik certs истекают, disk fill, registry GC fail, etc.)
- Любые ad-hoc уведомления от ops-скриптов
Domain: ntfy.vds.kzntsv.site (под уже существующим wildcard *.vds.kzntsv.site).
Open questions
Auth model:per-topic ACL + admin uservitya(resolved 2026-05-20). Реализация:NTFY_AUTH_DEFAULT_ACCESS=deny-all+ntfy user add --role=admin vitya. Admin role → rw to all topics, per-topic ACL не нужен. Anon (no creds) → 403 на publish и subscribe ✅.Топики:начали с двух:vds-backup(для vds-backup-rsync-kreknin) +vds-ops(general alerts: cert expiry, disk fill, container crashes). Можно добавлять без миграции — admin role даёт доступ ко всему автоматом.Phone app:Android (resolved 2026-05-20) → ntfy Android app, pure self-host без APNs middleware.Persistence:sqlite в/opt/stacks/ntfy/data/(cache.db + auth.db). Volume mount./data:/var/cache/ntfy.Phone-side smoke:confirmed 2026-05-20 22:54 MSK — оба push'а («Topic renamed», «Password updated») пришли в Android ntfy app, screenshot в conversation.
Implementation sketch
/opt/stacks/ntfy/docker-compose.yml:
services:
ntfy:
image: binwiederhier/ntfy
container_name: ntfy
restart: unless-stopped
command:
- serve
networks:
- proxy
volumes:
- ./data:/var/cache/ntfy
- ./etc:/etc/ntfy
environment:
NTFY_BASE_URL: https://ntfy.vds.kzntsv.site
NTFY_CACHE_FILE: /var/cache/ntfy/cache.db
NTFY_AUTH_FILE: /var/cache/ntfy/auth.db
NTFY_AUTH_DEFAULT_ACCESS: deny-all
NTFY_BEHIND_PROXY: true
NTFY_ATTACHMENT_CACHE_DIR: /var/cache/ntfy/attachments
labels:
- traefik.enable=true
- traefik.http.routers.ntfy.rule=Host(`ntfy.vds.kzntsv.site`)
- traefik.http.routers.ntfy.entrypoints=websecure
- traefik.http.routers.ntfy.tls.certresolver=letsEncrypt
- traefik.http.services.ntfy.loadbalancer.server.port=80
networks:
proxy:
external: true
После up — docker exec ntfy ntfy user add --role=admin vitya → задать пароль → грант access на топики.
Publish from any script:
curl -u "vitya:$NTFY_PASS" \
-H "Title: VDS backup completed" \
-H "Tags: white_check_mark" \
-H "Priority: default" \
-d "Size 12.5G, 18m12s, kreknin OK" \
https://ntfy.vds.kzntsv.site/vds-backup
Phone subscribes to https://ntfy.vds.kzntsv.site/vds-backup (с basic auth) — получает уведомление.
Decisions log
- 2026-05-20 — Image pinned
binwiederhier/ntfy:v2.11.0(conservative, matches pin policy остальных стэков: traefik v2.11, portainer 2.21.5). - 2026-05-20 — Auth:
NTFY_AUTH_DEFAULT_ACCESS=deny-all+ admin uservitya(random hex16 password). Admin role гранит rw ко всем топикам автоматом — per-topicntfy accesscalls returneduser vitya is an admin user, access control entries have no effect. Не нужны явные grants; разлогиниться от admin позже если будет потребность в ограниченных read-only users. - 2026-05-20 — Topics:
vds-backup+vds-ops(минимум для текущих нужд). Расширение бесплатное (admin → rw to all). - 2026-05-20 — TLS через traefik LE http-01 (issuer R12, valid Aug 18 2026); ntfy listens HTTP
:80внутриproxynetwork,NTFY_BEHIND_PROXY=trueчтобы trust X-Forwarded-* от traefik. - 2026-05-20 — Smoke green: anon publish/subscribe → 403, authed publish vds-backup+vds-ops → 200 с JSON id/ts.
- 2026-05-20 — User feedback: random hex password неудобен для ввода в phone app. Меняю на
Pryakhin9(consistency с TRAEFIK_DASHBOARD_PASS). Trade-off: меньше entropy, но scope ограничен (admin role, single user, push-сервис без чувствительных данных, can rotate если threat model изменится). Lesson — для human-input creds (phone/web UI) использовать память user'а, не random hex; random hex оставить для script-only (DB, API keys). - 2026-05-20 — Topics renamed
backup→vds-backup,ops→vds-ops(consistency с VDS scope, упрощает filtering если позже добавятся nas-* или kreknin-* топики). - 2026-05-20 — Phone-verified: Android ntfy app subscription → оба push'а пришли в течение секунд. End-to-end complete.
Completed steps
/opt/stacks/ntfy/{data}создано (owner vitya:vitya)docker-compose.ymlнаписан с traefik labels (Host, websecure, letsEncrypt, port 80)docker compose up -d→ container healthy, listening :80- Admin user
vityaдобавлен черезprintf "%s\n%s\n" "$PASS" "$PASS" | docker exec -i ntfy ntfy user add --role=admin vitya - LE cert issued via http-01, R12, valid 2026-05-20 → 2026-08-18
- Smoke test (publish/subscribe/anon-deny) green
- Creds saved:
/opt/stacks/ntfy/.env(chmod 600) + Windows~/projects/.common/secrets/vds-kzntsv.env - Password rotated random-hex →
Pryakhin9per user request (ntfy user change-pass) - Topics renamed
backup/ops→vds-backup/vds-ops - Phone-verified (Android ntfy app, screenshot 22:54 MSK)
Notes
- Триггер: после
[[vds-kzntsv-bootstrap]]Phase 3 и до или одновременно с[[vds-backup-rsync-kreknin]](backup pipeline хочет ntfy для статус-уведомлений). - Интегрируется с
[[vds-backup-rsync-kreknin]]— backup script публикует через ntfy + дублируется email'ом для надёжности. - Android setup hint: ntfy app → Add subscription → Topic
vds-backup(илиvds-ops) → Serverhttps://ntfy.vds.kzntsv.site→ enable «Use auth» → uservitya/ password из vds-kzntsv.env. - Cert renewal — automatic via traefik (letsEncrypt resolver), нет дополнительных шагов.