Le problème : trop d'outils de monitoring, trop de canaux de notification
Uptime Kuma surveille vos services, Beszel surveille vos métriques VPS, Backrest gère vos sauvegardes, et vos cron jobs se taisent la nuit — sauf quand ils échouent. Chacun a son canal de notification : Slack, e-mail, webhook, Telegram. Résultat : cinq intégrations différentes à maintenir, cinq formats d'alertes à lire, et aucune vue unifiée quand quelque chose casse à 03h00. ntfy résout ce problème à l'envers : c'est vous qui hébergez le hub de notifications, et chaque outil ou script y envoie ses alertes en HTTP brut. Un seul topic à écouter sur votre téléphone, zéro dépendance à un service tiers.
Ce que ntfy apporte hors de la boîte
- Envoi d'une notification avec un simple curl —
curl -d "Build échoué" https://ntfy.exemple.com/alertes-ci— zéro SDK, zéro bibliothèque. - Réception sur Android (F-Droid / Play Store), iOS (App Store), navigateur desktop et interface web ntfy — tous souscrivent au même topic via WebSocket ou SSE.
- Actions dans la notification — boutons qui déclenchent des callbacks HTTP, ouvrent des URLs ou exécutent des commandes sur l'appareil.
- Notifications planifiées — livraison différée (
X-Delay: 30min) pour programmer des rappels depuis des scripts. - Niveaux de priorité (min / basse / défaut / haute / urgente) avec contournement du mode Ne Pas Déranger pour les alertes critiques.
- Pièces jointes — images ou fichiers jusqu'à 15 Mo via l'en-tête
X-Attachou un upload multipart. - Notifications SMTP — ntfy peut envoyer un e-mail de secours si votre appareil mobile est hors ligne ou si vous n'avez pas l'app installée.
- Contrôle d'accès granulaire — utilisateurs, mots de passe hachés, tokens d'accès et ACL par topic via
ntfy useretntfy access. - < 30 Mo RAM, SQLite (ou PostgreSQL pour les déploiements à haut débit), conteneur unique — le serveur de notifications push le plus léger de sa catégorie.
Architecture : un pub/sub HTTP en conteneur unique
ntfy est un binaire Go qui tient en un seul conteneur Docker. Il expose une interface HTTP simple : PUT ou POST sur /votre-topic publie un message ; n'importe quel client qui écoute ce topic (via WebSocket, SSE ou l'app mobile) le reçoit instantanément. Les messages sont mis en cache dans SQLite (/var/lib/ntfy/cache.db) avec une rétention configurable (12 heures par défaut). Pour la production, ntfy s'attend à être placé derrière un reverse proxy nginx qui gère le TLS — le paramètre behind-proxy: true dans server.yml indique à ntfy de lire l'IP réelle depuis X-Forwarded-For. L'ensemble de la configuration avancée — authentification, SMTP, access control — vit dans un seul fichier /etc/ntfy/server.yml monté en volume Docker.
Déployer ntfy en production : 8 étapes
Déployer ntfy depuis le marketplace ServOrbit
Ouvrez votre tableau de bord ServOrbit → Marketplace → Monitoring → ntfy → Déployer. Docker tire
binwiederhier/ntfy:v2.30.0, démarre ntfy sur le port 8080 et l'interface web est accessible en moins de 5 secondes. Un reverse proxy nginx avec certificat Let's Encrypt est configuré automatiquement sur le domaine de votre choix.Envoyer votre première notification
Depuis n'importe quel terminal avec curl :
curl -d "Bonjour depuis mon VPS" https://ntfy.exemple.com/mon-topic
Ouvrez l'interface web ntfy sur la même URL, naviguez versmon-topicet votre message s'affiche en temps réel. Les topics sont créés au premier envoi — aucun paramétrage préalable.Créer le fichier server.yml complet
Montez un fichier de configuration dans le conteneur. Voici un
server.ymlde production :base-url: https://ntfy.exemple.comlisten-http: ":8080"behind-proxy: true
# Cache SQLite (12h)cache-file: /var/lib/ntfy/cache.dbcache-duration: 12h
# Authentification : personne ne peut lire ni écrire sans compteauth-file: /var/lib/ntfy/user.dbauth-default-access: deny-all
# SMTP sortant — ntfy envoie des e-mails de secourssmtp-sender-addr: smtp.exemple.com:587smtp-sender-user: [email protected]smtp-sender-pass: motdepasseSMTPsmtp-sender-from: [email protected]
Redémarrez le conteneur après toute modification de ce fichier :docker restart ntfy.
Pour que les notifications arrivent aussi sur iOS, ajoutezupstream-base-url: "https://ntfy.sh"à ce fichier : la section « iOS » plus bas explique pourquoi.Créer des utilisateurs et leurs tokens d'accès
Les tokens d'accès permettent à vos scripts d'envoyer des notifications sans exposer le mot de passe :
# Créer un utilisateur admin interactifdocker exec -it ntfy ntfy user add --role=admin admin
# Créer un utilisateur dédié aux scripts (rôle user)docker exec -it ntfy ntfy user add scripts
# Générer un token d'accès pour cet utilisateurdocker exec -it ntfy ntfy token add scripts
# Sortie : tk_AbCdEf123456...
# Autoriser cet utilisateur à publier sur un topic spécifiquedocker exec -it ntfy ntfy access scripts server-alerts rw
Le tokentk_AbCdEf123456est utilisé à la place du mot de passe dans tous vos scripts.Souscrire sur votre téléphone avec authentification
Installez l'app ntfy (Android : F-Droid ou Play Store ; iOS : App Store). Appuyez sur + → entrez l'URL de votre serveur → le topic. Quand ntfy demande des identifiants, entrez le nom d'utilisateur et le mot de passe du compte admin (ou un compte dédié lecture seule). Vous recevrez des notifications push dès qu'un message sera publié — via UnifiedPush (Android F-Droid), FCM (Android Play) ou APNs (iOS). Sur iOS, le push instantané n'arrive que si votre serveur a
upstream-base-urldéfini (voir la section dédiée plus bas).Alerter depuis vos cron jobs avec token
Utilisez le token d'accès dans l'en-tête
Authorization:
#!/bin/bashNTFY_URL="https://ntfy.exemple.com"NTFY_TOKEN="tk_AbCdEf123456"
# Exécuter le backup/usr/local/bin/backup.shSTATUS=$?if [ $STATUS -eq 0 ]; thencurl -s -H "Authorization: Bearer $NTFY_TOKEN" -H "Title: Backup nuit" -H "Priority: default" -H "Tags: white_check_mark" -d "Backup terminé à $(date)" "$NTFY_URL/cron-jobs"elsecurl -s -H "Authorization: Bearer $NTFY_TOKEN" -H "Title: Backup ÉCHOUÉ" -H "Priority: urgent" -H "Tags: warning" -d "Backup échoué — code de sortie $STATUS" "$NTFY_URL/cron-jobs"fi
Stockez le token dans/etc/ntfy-token(chmod 600, propriétaire root) et non en dur dans le script.Configurer Backrest et Uptime Kuma
Backrest (sauvegardes Restic via interface web) : dans l'interface Backrest → Plans → votre plan → Hooks → Add Hook → type
Webhook (ntfy)→ entrez l'URLhttps://ntfy.exemple.com/backupset le token Bearer. Backrest notifie à chaque fin de sauvegarde, succès comme erreur, avec les statistiques de la snapshot.Uptime Kuma : Paramètres → Notifications → Nouveau → type
ntfy→ Server URL :https://ntfy.exemple.com→ Topic :server-alerts→ Username / Password (ou laissez vide si vous utilisez un token dans un champ custom). Testez la notification depuis l'interface Uptime Kuma avant de valider.Grafana : Contact points → New → type
webhook→ URLhttps://ntfy.exemple.com/grafana→ HTTP headers :Authorization: Bearer tk_AbCdEf123456→Content-Type: text/plain.Activer les notifications e-mail de secours (SMTP)
Une fois
smtp-sender-addrconfiguré dansserver.yml, envoyez une notification avec l'en-têteX-Email:curl -H "Authorization: Bearer $NTFY_TOKEN" -H "Title: Alerte critique" -H "X-Email: [email protected]" -d "Disque plein à 95%" https://ntfy.exemple.com/server-alerts
ntfy envoie la notification push ET un e-mail. Utile pour les alertes où l'app mobile n'est pas installée ou pour archiver les incidents dans votre boîte mail.
Authentification et tokens d'accès : ne jamais exposer un topic public
Par défaut, ntfy démarre sans contrôle d'accès : la doc indique que auth-default-access vaut read-write, donc n'importe qui connaissant l'URL peut lire et écrire. En production, trois niveaux de protection s'empilent : d'abord auth-default-access: deny-all qui ferme le serveur à tout inconnu ; ensuite des utilisateurs créés via ntfy user add avec des mots de passe hachés stockés dans user.db (bcrypt, jamais en clair) ; enfin des tokens d'accès (ntfy token add) liés à un utilisateur spécifique, révocables indépendamment du mot de passe. Un token compromis se révoque sans changer le mot de passe de l'utilisateur. Les ACL par topic (ntfy access) permettent de cloisonner : l'utilisateur backrest ne peut écrire que sur backups, il ne voit pas server-alerts.
Configuration multi-tenant : un serveur, plusieurs équipes
ntfy supporte plusieurs « espaces » sur le même serveur via les namespaces de topics. Créez un utilisateur par service ou par équipe, accordez-lui les droits uniquement sur ses topics, et chaque service croit avoir son propre hub. Exemple : l'utilisateur ci-bot a le droit rw sur ci-builds et ci-deploys ; l'utilisateur ops-bot a le droit rw sur server-alerts et cron-jobs. L'admin (rôle admin) voit tout. La séparation est purement logique — un seul processus ntfy, une seule base SQLite, une seule URL de base. Pour des équipes vraiment séparées qui ne doivent pas partager l'instance, déployez deux conteneurs ntfy sur deux sous-domaines distincts.
Tip : noms de topics comme namespaces et stratégie de silence nocturne
Organisez vos alertes par source : ci-builds pour GitHub Actions/GitLab CI, server-alerts pour Uptime Kuma/Beszel, cron-jobs pour vos scripts batch, backups pour Backrest. L'app ntfy vous permet de mettre en sourdine certains topics la nuit (ex. cron-jobs) tout en gardant server-alerts en alerte maximale avec contournement du mode Ne Pas Déranger. Aucune configuration côté serveur — juste le nom du topic dans le curl et les préférences de l'app mobile.
Dépannage : 5 erreurs réelles en production
1. HTTP 403 Forbidden à l'envoi ou à la lecture
Message : {"code":40301,"http":403,"error":"Unauthorized"}
Cause : auth-default-access: deny-all est actif et le token manque ou est expiré. Vérifiez l'en-tête : curl -v -H "Authorization: Bearer tk_..." https://ntfy.exemple.com/mon-topic. Si le token est absent du header, le serveur rejette silencieusement. Régénérez le token avec ntfy token add <utilisateur> et mettez à jour vos scripts.
2. Notification envoyée mais jamais reçue sur mobile
Sur Android, vérifiez d'abord que l'app est abonnée à votre instance et non au serveur public ntfy.sh (URL dans les paramètres de l'app). Sur iOS, la cause la plus probable est l'absence de upstream-base-url: "https://ntfy.sh" dans server.yml : sans ce relais, la doc prévient que la livraison peut prendre des heures. Configurez-le, puis déroulez la checklist de la section iOS plus bas. base-url doit aussi être défini, mais la doc le relie aux URL de pièces jointes et au pied des e-mails, pas à l'arrivée du push.
3. Liens incorrects dans les e-mails sortants
Les e-mails SMTP envoyés par ntfy contiennent des liens vers les notifications. Sans base-url dans server.yml, le pied de l'e-mail (lien vers la notification) est construit sans l'URL publique et reste inutilisable depuis l'extérieur. Solution : définir base-url: https://ntfy.exemple.com et redémarrer le conteneur.
4. WebSocket drops répétés en production (reconnexions toutes les 30 s)
Cause : le timeout keepalive de nginx est inférieur à l'intervalle de heartbeat de ntfy (keepalive-interval: "45s" dans la configuration d'exemple de la doc). Le proxy ferme la connexion WebSocket avant que ntfy ne puisse envoyer son heartbeat. Ajoutez dans le bloc location nginx : proxy_read_timeout 60; et proxy_send_timeout 60;. Vérifiez aussi que proxy_http_version 1.1; et proxy_set_header Upgrade $http_upgrade; sont présents — sans eux, WebSocket est dégradé en long-polling SSE.
5. OOM (Out of Memory) sur un topic à très haut débit
ntfy garde les messages récents en mémoire avant de les écrire dans SQLite. Sur un topic qui reçoit des centaines de messages par seconde (ex. logs en temps réel mal filtrés), le buffer mémoire peut saturer un VPS à 512 Mo. Solution immédiate : réduire cache-duration à 1h ou 30m pour ce serveur. Solution structurelle : ne jamais utiliser ntfy comme système de log en temps réel — utilisez un filtre côté script (| grep -E 'ERROR|WARN') pour ne pousser que les événements pertinents. Pour les déploiements à très haut débit, ntfy peut utiliser PostgreSQL à la place de SQLite pour ses stockages (voir la doc de configuration).
iOS : pourquoi un ntfy auto-hébergé passe par ntfy.sh pour le push
Sur iOS, une app ne peut pas garder une connexion ouverte en arrière-plan : la documentation ntfy explique que cette restriction rend impossible un push instantané sans serveur central. Votre serveur auto-hébergé transmet donc, pour chaque message, une requête de « poll » à un serveur amont, ntfy.sh par défaut, qui se charge de réveiller l'app. Le réglage s'appelle upstream-base-url: "https://ntfy.sh". Cette requête ne contient que l'identifiant du message (en-tête X-Poll-ID) et l'empreinte SHA-256 de l'URL du topic, jamais le texte : le serveur auto-hébergé envoie littéralement « New message », et l'app va chercher le vrai contenu chez vous. Sans upstream-base-url, la documentation prévient que les notifications finissent par arriver, mais que la livraison peut prendre des heures (20 à 30 minutes si le téléphone est activement utilisé). Le paramètre upstream-access-token est facultatif : la doc ne le réserve qu'au cas où les limites du serveur amont sont dépassées ou si celui-ci est protégé.
Checklist iOS : sept vérifications avant de chercher plus loin
base-url— défini sur l'URL publique en HTTPS de votre serveur, celle que le téléphone peut joindre.upstream-base-url— présent dansserver.yml(ou en variable d'environnement), valeurhttps://ntfy.sh, conteneur redémarré.- Serveur par défaut de l'app — dans les réglages de l'app iOS, l'URL de votre serveur est celle qui est déclarée comme serveur par défaut.
- Abonnement au bon serveur — le topic a été ajouté en choisissant votre serveur, pas
ntfy.sh: un topic du même nom sur le serveur public est un autre topic. - Jeton amont —
upstream-access-tokenne sert que si les limites du relais sont atteintes ; sans cause établie, ne l'ajoutez pas par réflexe en espérant régler le problème. - Journaux du serveur — vérifiez que le message est bien publié puis transmis au relais. Si c'est le cas, votre côté fonctionne et le problème se situe entre le relais, Apple et le téléphone.
- Réglages du téléphone — Actualisation en arrière-plan et Notifications activées pour l'app, et aucun bloqueur DNS (AdGuard Home, Pi-hole) qui filtre
ntfy.sh.
Quand rien n'arrive malgré tout : ce que disent vraiment les issues #1305 et #1844
Ces deux issues du dépôt ntfy décrivent le symptôme que vous cherchez : un serveur auto-hébergé, un push iOS qui ne s'affiche pas, des messages qui n'apparaissent qu'en rafraîchissant l'app à la main. Lues le 30 octobre 2026, elles sont toutes deux ouvertes et aucune ne désigne une cause confirmée.
#1305 « No push notifications on ios? » (ouverte en mars 2025, étiquettes ios et bug). Le rapporteur n'obtient aucune bannière, ni avec ntfy.sh ni avec sa propre instance. Des commentaires disent qu'une réinstallation de l'app a aidé, d'autres que le problème est revenu des mois plus tard. Un contributeur propose que l'app se réabonne aux topics Firebase au lancement et a ouvert une PR brouillon, en précisant qu'il n'a pas pu la tester. Un mainteneur demande au rapporteur si l'abonnement Firebase est en cause, sans conclure.
#1844 « No iOS Push notification, also with valid subscription » (ouverte en juillet 2026, étiquettes ios, server et bug). Le rapporteur fait tourner ntfy dans Docker avec NTFY_UPSTREAM_BASE_URL sur https://ntfy.sh et NTFY_UPSTREAM_ACCESS_TOKEN renseigné. Il dit avoir atteint la limite gratuite de 250 messages par jour, puis pris un abonnement payant. Ses journaux montrent le message publié et envoyé au relais, et pourtant aucun push. Un contributeur répond que des erreurs 429 peuvent valoir un bannissement d'IP pouvant durer jusqu'à 4 heures, constate que le test curl du rapporteur passe (donc pas de bannissement), et que le compte affichait 48 messages envoyés sur 2 500. Il ajoute que les notifications iOS sont capricieuses sans que l'on sache exactement pourquoi. Contournement rapporté : désactiver puis réactiver « Actualisation en arrière-plan » et « Notifications » dans les réglages iOS. L'auteur écrit ne pas savoir pourquoi cela marche ; le problème est revenu, puis reparti sans cause connue, et un autre utilisateur confirme le contournement en septembre 2026. Un autre rapport relie un cas voisin à des listes de blocage DNS.
Ce qu'on peut en conclure, et ce qu'on ne peut pas. Ni une limite de messages par jour ni un blocage 429 ne sont établis comme cause dans ces fils : le 429 n'y est écarté pour l'IP du rapporteur, et la limite de 250 messages est un chiffre donné par lui, pas par la documentation de configuration. Le jeton amont n'y règle pas le problème. Traitez donc la liste ci-dessus comme des vérifications, pas comme un correctif garanti.
Tip : ne confiez pas une alerte critique au seul push iOS
Tant que ces issues restent ouvertes, doublez les alertes qui comptent : ajoutez l'en-tête X-Email (voir l'étape SMTP) pour recevoir aussi un e-mail, ou gardez un second canal sur le téléphone. Les contributeurs des deux issues suggèrent par ailleurs la PWA (web push) comme alternative à l'app iOS. Et testez de bout en bout après chaque mise à jour de l'app ou d'iOS, avec le heartbeat du watchdog décrit plus haut.
Intégrations avancées : Backrest, Uptime Kuma, Grafana
Backrest est le gestionnaire d'interface web pour les sauvegardes Restic. Il supporte nativement ntfy comme type de hook : configurez l'URL du topic et le token Bearer dans l'interface Backrest, et chaque snapshot réussie ou échouée génère automatiquement une notification push avec le nom de la snapshot, la taille dédupliquée et la durée. C'est le cas d'usage le plus propre : zero script, zero cron, l'intégration est dans l'outil lui-même.
Uptime Kuma supporte ntfy nativement dans son gestionnaire de notifications. La configuration prend moins d'une minute : nom d'utilisateur + mot de passe (ou laissez vide et ajoutez Authorization: Bearer dans les headers personnalisés). Chaque monitor (HTTP, port TCP, ping, certificat TLS) peut avoir plusieurs canaux de notification — combinez ntfy pour le push mobile et l'e-mail SMTP de ntfy pour la traçabilité.
Grafana utilise les webhooks génériques. Créez un contact point de type webhook, pointez sur https://ntfy.exemple.com/grafana, ajoutez l'en-tête Authorization: Bearer tk_... et Content-Type: text/plain. Les alertes Grafana (CPU > 90%, latence P99, disk > 80%) arrivent immédiatement sur votre téléphone avec le nom de la règle dans le titre.
Tip : tester la livraison de bout en bout avant de compter dessus
Avant de brancher votre infrastructure sur ntfy, testez la chaîne complète : envoyez une notification avec curl, vérifiez qu'elle apparaît dans l'interface web, qu'elle arrive sur l'app mobile, et que l'e-mail SMTP de secours atterrit dans la boîte mail. Un ntfy qui ne livre pas silencieusement est pire qu'une absence de monitoring — vous croyez être notifié, mais vous ne l'êtes pas. Planifiez un cron */30 * * * * qui envoie un heartbeat sur un topic watchdog — si l'app mobile ne reçoit pas ce heartbeat en 30 minutes, c'est ntfy lui-même qui est down.