Supervision et astreinte

Recevoir ses alertes sur le téléphone avec ntfy

Une alerte envoyée par la messagerie qui vient de tomber n'arrive jamais. ntfy fournit un canal à vous, indépendant, qui sait réveiller sans tout réveiller.

Niveau débutant  ·  Lecture 11 min  ·  Base ntfy 2

Toutes nos docs de supervision répètent la même consigne : un second canal d'alerte, qui ne dépend pas de l'infrastructure surveillée. Cette page dit comment le construire. ntfy est un petit serveur de notifications : on lui envoie un message par une simple requête HTTP, et il le pousse sur les téléphones abonnés. Un binaire, un fichier de configuration, une application mobile libre. Et surtout, une alerte qui sonne vraiment, même quand la messagerie de l'entreprise est précisément ce qui vient de tomber.

Renseignez l'adresse de votre serveur ntfy : configuration, commandes et exemples d'envoi s'adaptent. La valeur reste dans votre navigateur.

01Pourquoi un canal à soi

Le courriel est le pire canal d'astreinte : il dépend du serveur de messagerie, il arrive en retard, il se range dans un dossier, et il ne réveille personne. Les messageries d'équipe valent mieux, mais elles noient l'alerte de nuit au milieu des conversations du jour, et leurs notifications se règlent par application, pas par niveau d'urgence.

CanalDépend de votre infrastructureRéveille la nuitGravité distinguée
Courriel hébergé chez vousOuiNonNon
Messagerie d'équipeNonMalPar canal
SMS par un prestataireNonOuiNon
ntfy hébergé ailleursNonOui, par prioritéCinq niveaux

ntfy apporte ce qui manque aux autres : des sujets, auxquels chacun s'abonne selon son rôle, et des priorités, que le téléphone traite différemment. Une priorité maximale passe outre le mode silencieux sur Android, une priorité basse arrive sans bruit.

02Où l'héberger

La règle est la même que pour Uptime Kuma : jamais sur l'infrastructure surveillée. Le serveur ntfy peut d'ailleurs partager le petit VPS externe d'Uptime Kuma : les deux tiennent ensemble dans 1 Go de mémoire, et forment un couple cohérent, une sonde et un canal indépendants de la production.

Et le service public ntfy.sh ?

Il fonctionne très bien et suffit pour essayer. Pour des alertes de production, nous préférons un serveur à nous : les sujets publics sont devinables par construction, et le contenu d'une alerte (nom d'hôte, adresse, nature de la panne) décrit votre infrastructure à qui l'intercepte.

03Installer le serveur

Fichier docker-compose.yml
services:
  ntfy:
    image: binwiederhier/ntfy
    container_name: ntfy
    command: serve
    restart: unless-stopped
    environment:
      TZ: Europe/Paris
    volumes:
      - ./etc:/etc/ntfy
      - ./cache:/var/cache/ntfy
      - ./lib:/var/lib/ntfy
    ports:
      - "127.0.0.1:2586:80"
Fichier etc/server.yml
base-url: "https://ntfy.exemple.fr"
behind-proxy: true

# messages conserves 12 h pour les telephones momentanement hors ligne
cache-file: "/var/cache/ntfy/cache.db"
cache-duration: "12h"

# controle d'acces : rien n'est permis sans compte ni jeton
auth-file: "/var/lib/ntfy/user.db"
auth-default-access: "deny-all"

# notifications instantanees sur iPhone, voir la section suivante
upstream-base-url: "https://ntfy.sh"
Bloc Nginx
server {
    listen 443 ssl;
    http2 on;
    server_name ntfy.exemple.fr;

    ssl_certificate     /etc/letsencrypt/live/ntfy.exemple.fr/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/ntfy.exemple.fr/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:2586;
        proxy_http_version 1.1;
        # connexions longues : les telephones restent abonnes en continu
        proxy_buffering off;
        proxy_request_buffering off;
        proxy_read_timeout 3m;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}
Le tampon du proxy inverse est l'ennemi

Sans proxy_buffering off, Nginx retient les messages en attendant de remplir son tampon. Les notifications arrivent alors par paquets, avec plusieurs minutes de retard, ce qui est exactement ce qu'on cherchait à éviter.

04Utilisateurs, sujets et jetons

Avec deny-all, chaque usage reçoit son propre compte et ses propres droits. Nous séparons systématiquement ceux qui publient (les outils) de ceux qui lisent (les humains), et nous nommons les sujets par public, pas par outil.

Créer les comptes et les droits
# un administrateur, pour l'exploitation du serveur lui-meme
docker compose exec ntfy ntfy user add --role=admin admin

# les humains : lecture seule sur les sujets qui les concernent
docker compose exec ntfy ntfy user add marie
docker compose exec ntfy ntfy access marie 'astreinte' read-only
docker compose exec ntfy ntfy access marie 'equipe-*' read-only

# les outils : un compte par emetteur, ecriture seule
docker compose exec ntfy ntfy user add supervision
docker compose exec ntfy ntfy access supervision 'astreinte' write-only
docker compose exec ntfy ntfy access supervision 'equipe-*' write-only
docker compose exec ntfy ntfy token add supervision

# relire l'ensemble des droits
docker compose exec ntfy ntfy access
SujetQui s'y abonneCe qui y arrive
astreinteLa personne d'astreinte, téléphone en sonneriePannes franches, priorité haute ou maximale
equipe-infraToute l'équipe, sans sonnerie la nuitDégradations, avertissements, rapports
sauvegardesLe responsable des sauvegardesÉchecs et retards de tâches planifiées
Un jeton d'écriture qui fuit ne lit rien

C'est tout l'intérêt de la séparation. Le jeton posé dans dix scripts et trois outils finira un jour dans un dépôt ou un journal ; il permettra d'envoyer un faux message, pas de lire vos alertes ni de supprimer quoi que ce soit.

05Le téléphone

L'application ntfy existe sur Android et iOS. On y ajoute le serveur et les identifiants de l'utilisateur, puis on s'abonne aux sujets.

  • Android : l'application maintient elle-même une connexion avec votre serveur. Choisissez la distribution F-Droid ou activez la « livraison instantanée » pour ne pas dépendre des services de Google, puis exemptez l'application de l'optimisation de batterie, sans quoi le système finit par la mettre en sommeil.
  • iOS : Apple n'autorise pas une application à garder une connexion ouverte en arrière-plan. Les notifications passent obligatoirement par le service de notification d'Apple, qui ne connaît que ntfy.sh. C'est le rôle de upstream-base-url : votre serveur envoie à ntfy.sh un simple signal de réveil, sans le contenu du message, et le téléphone vient ensuite chercher le message chez vous.
Testez le téléphone verrouillé, de nuit

Le test qui compte : écran éteint, mode Ne pas déranger activé, priorité maximale. Sur Android, l'application propose un réglage par sujet pour passer outre le mode silencieux ; sur iOS, il faut autoriser les alertes critiques. Une astreinte n'existe que si ce test sonne.

06Envoyer une alerte

Envoyer une alerte, c'est une requête HTTP. Pour un message simple, les en-têtes suffisent ; dès que le titre contient des accents, publiez plutôt en JSON sur la racine du serveur, ce qui évite les soucis d'encodage des en-têtes HTTP.

Depuis un terminal ou un script
# le plus court
curl -H "Authorization: Bearer tk_votre_jeton" \
     -d "Sauvegarde de srv-fichiers terminee" \
     https://ntfy.exemple.fr/sauvegardes

# en JSON : titre accentue, priorite, etiquettes et lien
curl -H "Authorization: Bearer tk_votre_jeton" \
     -H "Content-Type: application/json" \
     -d '{
           "topic": "astreinte",
           "title": "pve-02 injoignable",
           "message": "Plus de reponse depuis 5 minutes. Quorum toujours present.",
           "priority": 5,
           "tags": ["rotating_light", "proxmox"],
           "click": "https://zabbix.exemple.fr/zabbix.php?action=problem.view"
         }' \
     https://ntfy.exemple.fr/

Les étiquettes qui correspondent à un nom d'émoji s'affichent comme tel devant le titre : rotating_light pour un gyrophare, white_check_mark pour un retour à la normale. C'est anecdotique sur un écran de bureau, et précieux sur un écran de verrouillage où l'on doit comprendre en une seconde s'il faut se lever.

07Brancher les outils de supervision

OutilMéthodeRemarque
Uptime KumaType de notification ntfy intégréServeur, sujet, priorité et jeton d'accès se saisissent dans le formulaire
BeszelURL Shoutrrr ntfy://utilisateur:motdepasse@ntfy.exemple.fr/sujetUn compte d'écriture dédié, avec mot de passe
AlertmanagerRécepteur webhook et passerelleVoir ci-dessous
ZabbixType de média webhookScript ci-dessous si votre version n'en fournit pas
Scripts, cron, sauvegardescurlUne ligne en fin de script

Alertmanager

Alertmanager envoie son propre format JSON, que ntfy doit transformer en notification. Deux voies : les versions récentes de ntfy savent appliquer un modèle de message au corps reçu, ce qui permet de pointer le webhook directement sur le serveur ; à défaut, une petite passerelle comme alertmanager-ntfy fait la traduction. Vérifiez dans la documentation de votre version de ntfy si la première est disponible : elle évite un composant de plus.

Zabbix

Un type de média webhook se crée dans Alertes, Types de médias, avec quatre paramètres : url, jeton, sujet (renseigné par le média de l'utilisateur, {ALERT.SENDTO}), et titre et message issus du message de l'action.

Script du type de média webhook
var p = JSON.parse(value);
var gravite = {"Disaster": 5, "High": 5, "Average": 4, "Warning": 3,
               "Information": 2, "Not classified": 1};

// {EVENT.VALUE} vaut 1 pour un probleme, 0 pour un retour a la normale
var retabli = (p.statut === "0");

var corps = {
    topic: p.sujet,
    title: p.titre,
    message: p.message,
    priority: retabli ? 2 : (gravite[p.gravite] || 3),
    tags: [retabli ? "white_check_mark" : "rotating_light"],
    click: p.lien
};

var req = new HttpRequest();
req.addHeader("Content-Type: application/json");
req.addHeader("Authorization: Bearer " + p.jeton);
var reponse = req.post(p.url, JSON.stringify(corps));

if (req.getStatus() !== 200) {
    throw "ntfy a repondu " + req.getStatus() + " : " + reponse;
}
return "OK";
ParamètreValeur
urlhttps://ntfy.exemple.fr/
jetonLe jeton d'écriture, de préférence via une macro secrète
sujet{ALERT.SENDTO}
titre{ALERT.SUBJECT}
message{ALERT.MESSAGE}
gravite{EVENT.SEVERITY}
statut{EVENT.VALUE} : 1 pour un problème, 0 pour un retour à la normale, envoyé sans sonnerie
lienURL de l'événement, comme dans le chapitre alertes du guide Zabbix
Une gravité par niveau d'urgence, pas par outil

Le script reprend la grille de gravité du guide Zabbix : Haute et Désastre en priorité 5, qui sonne, le reste en dessous. Gardez la même correspondance pour tous les outils, sans quoi un avertissement d'Uptime Kuma réveillera plus fort qu'une panne signalée par Zabbix.

08Les priorités, ou l'art de ne pas réveiller pour rien

PrioritéNomComportement par défautNotre usage
5max / urgentSonnerie longue, vibration, fenêtre surgissanteService rendu indisponible
4highSonnerie et vibrationService dégradé, en heures ouvrées
3defaultNotification normaleAvertissements, sauvegardes en échec
2lowSans sonRapports, retours à la normale
1minRangée sans bruit ni icôneJournal d'activité, tests

La tentation est de tout envoyer en priorité 5, « pour être sûr ». C'est ainsi que l'on apprend à son cerveau qu'une sonnerie de nuit ne veut rien dire, et qu'on finit par couper le son. Une semaine d'astreinte sans une seule priorité 5 injustifiée est le meilleur indicateur de qualité d'une supervision, bien avant le nombre de sondes ou la beauté des tableaux de bord.

Une supervision qui veille pendant que vous dormez

Nous concevons, installons et exploitons des plateformes de supervision pour des PME, des hébergeurs et des collectivités : Zabbix, Prometheus et Grafana, ou plus léger quand le besoin l'est. Reprise d'une supervision existante, réglage des alertes qui sonnent pour rien, astreinte : nous intervenons au forfait comme au long cours.