Uptime Kuma : supervision auto-hébergée
Lancez Uptime Kuma dans Docker pour surveiller sites, ports, DNS et cron, alerter par email ou Telegram et publier une page de statut sur un VPS séparé.
Ce que vous allez construire
Un seul petit conteneur qui surveille vos autres serveurs et sites web depuis l'extérieur et vous prévient dès que l'un d'eux cesse de répondre, par email, Telegram, Discord ou un webhook. Uptime Kuma est un seul processus Node adossé à un fichier SQLite, il tourne donc confortablement dans 256 à 512 Mo de RAM, et vous offre un tableau de bord en direct, des graphiques d'historique et une page de statut publique. L'installation tient dans un fichier Compose de dix lignes ; ce qui compte vraiment, c'est où vous l'exécutez et si vos alertes ont déjà été déclenchées lors d'un test, car un moniteur dont vous n'avez jamais prouvé qu'il peut vous joindre est pire que rien : il vous donne un sentiment de sécurité alors qu'il ne surveille rien.
Exécutez le moniteur là où la panne ne peut pas l'atteindre
Cette seule décision fait ou défait tout le reste, elle vient donc en premier. N'exécutez pas Uptime Kuma sur la même machine que ce qu'il surveille. Si le moniteur vit sur le serveur qu'il surveille, l'événement exact qui vous intéresse, cette machine qui meurt ou qui manque de mémoire, tue aussi le moniteur et vous ne recevez aucune alerte : le silence d'un moniteur mort se lit exactement comme « tout va bien ». Il existe un piège plus subtil même quand la machine est en vie : un moniteur pointé sur localhost partage le CPU avec la charge de travail, donc un pic de charge fait expirer sa propre vérification et bascule la cible en down, une fausse alerte, alors que les vrais utilisateurs sont servis sans problème.
Exécutez donc Uptime Kuma sur un VPS différent de celui qu'il surveille, idéalement chez un autre fournisseur ou dans une autre région, en atteignant vos services comme le font vos utilisateurs : par l'internet public, par nom d'hôte. Une instance bon marché suffit, et un seul petit VPS de supervision peut surveiller tous vos serveurs. Pour détecter un Kuma lui-même mort, ajoutez un heartbeat push depuis un cron ailleurs.
Prérequis et dimensionnement
- Un VPS Ubuntu 24.04 tout neuf avec Docker Engine et le plugin Compose v2, installés depuis le dépôt apt officiel de Docker, et non le paquet
docker.iode la distribution, qui est en retard. - 256 Mo de RAM font tourner une poignée de moniteurs ; 512 Mo à 1 Go sont confortables pour des dizaines plus le proxy inverse, et le CPU est quasi inactif entre les vérifications.
- Un domaine et un enregistrement DNS
A(disonsstatus.example.compointant vers le VPS), uniquement si vous voulez du TLS et une page de statut publique. Une instance privée peut se passer de DNS et utiliser un VPN ou un tunnel SSH. - Un réseau sortant vers l'endroit où partent les alertes : SMTP vers votre fournisseur de messagerie, ou HTTPS vers Telegram et Discord.
Le fichier Compose
Placez ceci dans /srv/uptime-kuma/compose.yaml.
services:
uptime-kuma:
image: louislam/uptime-kuma:2
container_name: uptime-kuma
restart: unless-stopped
ports:
- "127.0.0.1:3001:3001"
volumes:
- kuma-data:/app/data
volumes:
kuma-data:Lancez-le et observez le premier démarrage :
sudo mkdir -p /srv/uptime-kuma
# save the file above as /srv/uptime-kuma/compose.yaml, then:
cd /srv/uptime-kuma && sudo docker compose up -d
sudo docker compose logs -f uptime-kumaUn démarrage correct journalise Listening on 3001 puis devient silencieux. Trois choses dans ce fichier sont délibérées.
127.0.0.1:3001:3001, et non 3001:3001. Docker publie les ports avec des règles DNAT évaluées avant que ufw ne voie le paquet, donc un simple 3001:3001 place votre tableau de bord sur l'internet public quel que soit votre pare-feu. Le lier à la boucle locale le garde privé, avec seulement le proxy inverse exposé ; une instance privée peut se passer du proxy et atteindre 3001 via un VPN WireGuard auto-hébergé à la place.
Un volume nommé sur /app/data. Tout ce dont Uptime Kuma se souvient, la base de données SQLite, vos moniteurs, les paramètres de notification et les logos de la page de statut, y réside. Perdez-le et vous repartez d'un écran d'administration vide ; c'est la seule chose que vous devez sauvegarder.
L'image est épinglée à un tag majeur, :2. C'est la ligne stable actuelle ; vérifiez sur Docker Hub le dernier numéro majeur avant de copier ceci, et ne suivez jamais un tag mouvant comme latest, que le projet déconseille. Un saut de version majeure sur cette image est une migration de base de données à sens unique que vous voulez déclencher délibérément, et non subir au détour d'un pull de routine.
Une réserve : /app/data doit se trouver sur un système de fichiers avec des verrous de fichiers POSIX. Un volume Docker local convient ; sur NFS la base SQLite se corrompt et vous obtenez SQLITE_BUSY et database disk image is malformed, donc n'utilisez jamais un partage réseau.
Premier lancement : créez le compte administrateur
Ouvrez l'instance dans votre navigateur via votre proxy à https://status.example.com, ou via un tunnel SSH : lancez ssh -L 3001:127.0.0.1:3001 user@your-vps et ouvrez http://localhost:3001. La première page est un formulaire de configuration pour le nom d'utilisateur et le mot de passe de l'administrateur ; il n'y a pas d'identifiant par défaut. Choisissez un vrai mot de passe : ce tableau de bord voit les adresses internes et les jetons de tout ce que vous surveillez. Oublié plus tard ? Réinitialisez-le depuis l'hôte, pas depuis le navigateur :
sudo docker compose exec uptime-kuma npm run reset-passwordAjoutez d'abord vos canaux de notification, et testez-les
Configurez les alertes avant d'ajouter des moniteurs, afin de pouvoir rattacher un canal à chaque moniteur que vous créez. Allez dans Settings puis Notifications puis Setup Notification, et utilisez le bouton Test de chaque canal pour confirmer que le message arrive, car une notification non testée est la deuxième façon la plus courante dont une installation échoue silencieusement.
Email (SMTP). Renseignez l'hôte, le port, le chiffrement, le nom d'utilisateur, le mot de passe, un From et un To. Les deux combinaisons qui fonctionnent sont 465 avec « Secure » réglé sur TLS/SSL, ou 587 avec STARTTLS. Pour Gmail et la plupart des fournisseurs avec authentification à deux facteurs, vous devez générer un mot de passe d'application ; un mot de passe de compte normal renvoie Error: Invalid login: 535-5.7.8 Username and Password not accepted.
Telegram. Écrivez à @BotFather, envoyez /newbot, copiez le jeton du bot. Pour votre chat ID, écrivez une fois au nouveau bot, ouvrez https://api.telegram.org/bot<token>/getUpdates, et lisez chat.id dans le JSON. Un bot auquel vous n'avez jamais écrit d'abord a un getUpdates vide et nulle part où envoyer.
Discord. Dans le salon, ouvrez Edit Channel puis Integrations puis Webhooks puis New Webhook, copiez l'URL, et collez-la comme notification Discord.
Webhook générique. Pour tout le reste, un webhook entrant Slack, un point de terminaison personnalisé, un hook de domotique, le type Webhook envoie en POST une charge utile JSON vers une URL que vous fournissez, et l'intégration Apprise incluse couvre la plupart des quatre-vingt-dix autres services de la liste.
Ajoutez des moniteurs, un type à la fois
Cliquez sur Add New Monitor, choisissez un type, et définissez le Friendly Name, le Check Interval (60 secondes est raisonnable), les Retries (échecs consécutifs avant « down » ; 2 ou 3 pour qu'un paquet perdu ne déclenche pas une alerte), et les notifications à déclencher. Les types que vous utiliserez :
- HTTP(s). Une URL complète. Up signifie un code de statut accepté (200-299 par défaut ; élargissez-le sous Accepted Status Codes si
301ou401est normal chez vous). Votre cheval de bataille pour les sites web et les API. - HTTP(s) - Keyword. La même requête, mais « up » exige aussi qu'une chaîne soit présente, ou avec Invert absente, dans le corps. Cela attrape le site qui renvoie
200 OKtout en affichant « Error establishing a database connection », qu'une simple vérification HTTP considère comme saine. - TCP Port. Une simple connexion TCP vers un hôte et un port, pour ce qui n'est pas HTTP : SSH sur 22, Postgres sur 5432, un serveur SMTP sur 25, un serveur de jeu.
- Ping. Écho ICMP : joignabilité et latence à moindre coût. Mais beaucoup de réseaux et de pare-feu cloud rejettent l'ICMP, donc un moniteur ping rouge peut signifier « hôte down » ou « le fournisseur bloque le ping » ; confirmez avec un moniteur TCP.
- DNS. Résout un enregistrement (A, AAAA, MX, TXT et ainsi de suite) auprès d'un résolveur que vous nommez, et peut vérifier la réponse, détectant tôt une panne de registraire ou de DNS.
- Push. Le moniteur inversé, couvert juste après.
Surveiller une tâche cron avec un moniteur push (heartbeat)
Chaque moniteur ci-dessus atteint votre service depuis l'extérieur. Un moniteur push fonctionne dans l'autre sens : Uptime Kuma attend, et votre tâche l'appelle pour dire « j'ai tourné ». C'est la seule façon honnête de surveiller une sauvegarde ou un cron : une vérification HTTP sait qu'une URL répond, mais seule la tâche sait qu'elle s'est terminée.
Créez un moniteur de type Push. Uptime Kuma génère une URL unique comme :
https://status.example.com/api/push/j8Xa2Kd9Qe?status=up&msg=OK&ping=Réglez le Heartbeat Interval sur la fréquence d'exécution de la tâche, plus un peu de marge. Puis ajoutez une ligne à la fin du script, pour qu'elle ne se déclenche qu'en cas de succès :
#!/usr/bin/env bash
set -euo pipefail
# ... your backup or job runs here; set -e aborts on any failure ...
curl -fsS --retry 3 "https://status.example.com/api/push/j8Xa2Kd9Qe?status=up&msg=backup+ok&ping="Si la tâche échoue, set -e interrompt avant le curl ; si la machine est en panne, il ne s'exécute pas non plus. Dans les deux cas le heartbeat s'arrête, et une fois la fenêtre intervalle-plus-retries écoulée, Uptime Kuma bascule le moniteur en down et vous alerte. Traitez ce jeton push comme un secret : quiconque le possède peut forger un battement en bonne santé.
Construire une page de statut publique
Une page de statut est la vue côté client : quels services sont up et leur historique récent, sans exposer votre tableau de bord. Allez dans Status Pages puis New Status Page, donnez-lui un nom et un slug (le chemin public, comme /status/main), glissez les moniteurs voulus dans des groupes comme « Websites » et « APIs », ajoutez un logo et une courte description, et enregistrez. Vous pouvez aussi lier la page à son propre domaine pour que status.example.com la serve directement.
Deux mises en garde : n'ajoutez que les moniteurs que vous acceptez de rendre publics, car une page de statut révèle qu'un service existe et s'il est up ; et le tableau de bord reste derrière votre identifiant tandis que la page de statut est intentionnellement publique et ne nécessite aucune authentification.
Placez-le derrière un proxy inverse avec TLS, et attention aux websockets
Pour une instance publique, placez un proxy inverse devant le conteneur lié à la boucle locale pour le TLS et un nom d'hôte. Le détail qui piège tout le monde : l'interface d'Uptime Kuma est une application Socket.IO en direct, donc le proxy doit mettre à niveau la connexion WebSocket. Oubliez-le et la page se charge mais ne se connecte jamais ; le tableau de bord reste sur « Connecting... », les heartbeats en direct ne se mettent jamais à jour, et la console du navigateur affiche WebSocket connection to 'wss://.../socket.io/...' failed.
Installez nginx et certbot, puis écrivez le vhost qui fait proxy vers le port de la boucle locale. Placez-le sur le port 80 pour l'instant et laissez certbot ajouter le TLS ensuite ; le défi, le minuteur de renouvellement et ses modes d'échec sont couverts dans l'émission de certificats Let's Encrypt avec certbot et nginx.
sudo apt install -y nginx certbot python3-certbot-nginxEnregistrez ceci sous /etc/nginx/sites-available/status.example.com ; les deux lignes WebSocket sont celles qui comptent :
server {
listen 80;
server_name status.example.com;
location / {
proxy_pass http://127.0.0.1:3001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 3600s;
}
}Activez le site, testez la configuration, puis laissez certbot réécrire le bloc pour écouter sur 443, insérer le certificat et ajouter une redirection HTTP vers HTTPS :
sudo ln -s /etc/nginx/sites-available/status.example.com /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d status.example.comLa paire Upgrade et Connection "upgrade" est tout l'enjeu, et proxy_read_timeout 3600s empêche nginx de démolir le socket de longue durée ; certbot copie les deux dans le bloc 443 qu'il génère. Si vous faites déjà tourner plusieurs conteneurs derrière un seul proxy, leur routage via Traefik avec TLS automatique fait la même chose avec des labels de conteneur et transmet les mises à niveau WebSocket par défaut.
Ne mettez pas d'authentification basique sur tout le vhost, car cela verrouille aussi la page de statut publique et le point de terminaison /api/push. Gardez l'identifiant intégré d'Uptime Kuma, ajoutez fail2ban surveillant les échecs de connexion répétés s'il est exposé sur internet, et si le tableau de bord n'a jamais besoin d'être public, supprimez le proxy et atteignez-le via un VPN.
Surveillance de l'expiration des certificats, bien faite
Un moniteur HTTP(s) peut aussi vous prévenir avant qu'un certificat TLS n'expire : cochez Certificate Expiry Notification et Uptime Kuma vous alerte un nombre de jours défini à l'avance. Deux erreurs le font mal interpréter. Surveillez par nom d'hôte, pas par IP, sinon une requête sans SNI reçoit le certificat par défaut du serveur et vous voyez Hostname/IP does not match certificate's altnames. Et ne cochez pas Ignore TLS/SSL Error sur un moniteur dont vous voulez des avertissements d'expiration : cette bascule est pour les hôtes internes auto-signés (unable to verify the first certificate, DEPTH_ZERO_SELF_SIGNED_CERT), mais elle empêche Uptime Kuma de vérifier le certificat du tout, expiration comprise.
Sauvegardes : c'est un seul répertoire
Comme tout réside dans /app/data, une sauvegarde est une copie de ce volume prise pendant que le conteneur est arrêté, pour que le fichier SQLite soit cohérent :
cd /srv/uptime-kuma
sudo docker compose stop
sudo docker run --rm \
-v uptime-kuma_kuma-data:/data \
-v /var/backups/kuma:/backup \
alpine tar czf /backup/kuma-$(date -u +%Y%m%dT%H%M%SZ).tgz -C /data .
sudo docker compose startConfirmez d'abord le vrai nom du volume avec docker volume ls | grep kuma, puisque Compose le préfixe avec le répertoire du projet. Puis copiez l'archive hors de la machine, car une sauvegarde sur le même VPS est une copie, pas une sauvegarde. La restauration est l'inverse : arrêtez la stack, extrayez dans un volume /app/data vide, démarrez-la.
Mises à niveau
Les mises à niveau sont un pull d'image :
cd /srv/uptime-kuma
sudo docker compose pull
sudo docker compose up -dLe nouveau conteneur exécute toute migration de base de données au premier démarrage ; observez docker compose logs -f. Prenez la sauvegarde ci-dessus avant de faire le pull, et restez dans un tag majeur : passer de :1 à :2 est une migration à sens unique, donc sauvegardez d'abord et vérifiez les notes de version.
Modes de défaillance, avec les messages que vous verrez
Faux « down » sur un moniteur pointé sur localhost. Le moniteur passe au rouge avec timeout of 48000ms exceeded ou connect ETIMEDOUT, alors que le service répond depuis votre ordinateur portable. S'il cible le même hôte sur lequel tourne Uptime Kuma, un pic de CPU ou de mémoire a affamé la vérification, pas la cible. Déplacez le moniteur vers un VPS séparé et ciblez le nom d'hôte public.
connect ECONNREFUSED 127.0.0.1:443 (ou n'importe quel port). Rien n'écoutait sur ce port : soit le service est en panne, soit vous avez surveillé localhost depuis l'intérieur du conteneur, où 127.0.0.1 est le conteneur, pas votre serveur. Surveillez le nom d'hôte public, pas la boucle locale.
Invalid login: 535-5.7.8 Username and Password not accepted lors d'un test email. Les identifiants SMTP sont faux, ou le fournisseur veut un mot de passe spécifique à l'application et a reçu votre mot de passe de compte. Générez un mot de passe d'application et collez-le.
connect ETIMEDOUT ou queryA ETIMEDOUT <host> lors d'un test email. Mauvais port, ou le fournisseur bloque le SMTP sortant. Vérifiez que 465 ou 587 correspond au réglage Secure/STARTTLS, et testez depuis l'hôte avec nc -vz smtp.example.com 587. Beaucoup de fournisseurs bloquent le 25 sortant et certains bloquent les ports de soumission jusqu'à ce que vous le demandiez.
self signed certificate ou unable to verify the first certificate lors d'un test email. Votre serveur SMTP présente un certificat auquel Node ne fera pas confiance ; corrigez le certificat du serveur de messagerie plutôt que de le contourner.
Tableau de bord bloqué sur « Connecting... », la console affiche WebSocket connection ... failed. Le proxy inverse ne met pas à niveau le WebSocket. Ajoutez les en-têtes Upgrade et Connection "upgrade" sur nginx, ou utilisez un proxy qui les transmet par défaut comme Traefik ou Caddy. Le HTML se charge parce que c'est un GET HTTP normal ; seul le socket en direct a besoin de la mise à niveau.
Le moniteur d'expiration de certificat ne prévient jamais, ou prévient à tort. Soit Ignore TLS/SSL Error est coché, ce qui désactive la vérification du certificat, soit le moniteur cible une IP et lit le mauvais certificat à cause d'un SNI manquant, affichant Hostname/IP does not match certificate's altnames. Décochez ignore, surveillez par nom d'hôte.
SQLITE_BUSY ou database disk image is malformed dans les journaux. Le volume /app/data est sur un système de fichiers sans verrouillage de fichiers correct, généralement NFS ; déplacez-le vers un volume Docker local et restaurez depuis une sauvegarde.
FAQ
Où faut-il faire tourner mon moniteur de disponibilité ?
Sur un serveur différent de ceux qu'il surveille, idéalement un autre fournisseur ou une autre région, en les atteignant par nom d'hôte via l'internet public tout comme le font vos utilisateurs. Si le moniteur partage une machine avec ses cibles, la panne qui tue le serveur tue aussi le moniteur, et un hôte surchargé le fait crier « down » à propos de services qui vont bien. Un petit VPS séparé évite les deux.
Comment recevoir des alertes sur Telegram ou par email ?
Ajoutez le canal sous Settings puis Notifications, puis rattachez-le à chaque moniteur. Pour Telegram, créez un bot avec @BotFather et lisez chat.id dans https://api.telegram.org/bot<token>/getUpdates ; pour l'email, utilisez 465 pour SSL ou 587 pour STARTTLS avec un mot de passe d'application si votre fournisseur utilise l'authentification à deux facteurs. Appuyez sur Test et confirmez que le message arrive avant de vous y fier.
Uptime Kuma peut-il surveiller une tâche cron ou un script de sauvegarde ?
Oui, c'est le moniteur Push : Uptime Kuma vous donne une URL et vous la curl à la fin du script pour qu'elle ne se déclenche qu'en cas de succès. Si la tâche échoue ou si la machine est en panne, le heartbeat n'arrive jamais, et vous êtes alerté après l'écoulement de l'intervalle. C'est la seule façon fiable de savoir qu'une tâche planifiée a réellement tourné, puisqu'une vérification externe ne peut pas voir à l'intérieur.
Uptime Kuma ou Zabbix, lequel choisir ?
Uptime Kuma répond à « est-ce up, depuis l'extérieur, et m'a-t-il alerté » en dix minutes avec presque aucune ressource, plus une page de statut. Il ne collecte pas de métriques approfondies comme les tendances de CPU, de mémoire et de disque ou des seuils à l'échelle d'un parc ; pour cela, un serveur de supervision Zabbix complet est l'outil plus lourd, basé sur des agents, et beaucoup de gens font tourner les deux. Vous hésitez encore sur ce qu'il faut faire tourner ? notre panorama de ce qu'il faut auto-héberger en 2026 remet la supervision en contexte.