Uptime Kuma in Docker: monitoraggio self-hosted
Configura Uptime Kuma in Docker per controllare siti, porte, DNS e cron, ricevere avvisi via email o Telegram e pubblicare una status page da un VPS separato.
Cosa stai realizzando
Un singolo container leggero che controlla dall'esterno gli altri server e i siti web e ti avvisa non appena uno smette di rispondere, tramite email, Telegram, Discord o un webhook. Uptime Kuma è un unico processo Node supportato da un file SQLite, quindi funziona senza problemi con 256-512 MB di RAM e offre una dashboard aggiornata in tempo reale, grafici dello storico e una pagina pubblica di stato. L'installazione richiede un file Compose di dieci righe; l'aspetto realmente importante è dove lo esegui e se gli avvisi si sono mai attivati durante un test, perché un monitor di cui non hai mai verificato la capacità di raggiungerti è peggio di non averne alcuno: ti fa credere di essere protetto mentre non controlla nulla.
Esegui il monitoraggio in un ambiente che l'interruzione non possa raggiungere
Questa decisione determina il successo dell'intera configurazione, quindi viene prima di tutto. Non eseguire Uptime Kuma sullo stesso server dei servizi che monitora. Se il monitor risiede sul server che deve controllare, proprio l'evento che ti interessa, ovvero il blocco del server o l'esaurimento della memoria, interrompe anche il monitoraggio e non ricevi alcun avviso: il silenzio di un monitor non raggiungibile è identico al messaggio «tutto funziona». Esiste anche un problema meno evidente quando il server è ancora operativo: un monitor che controlla localhost condivide la CPU con il carico di lavoro, quindi un picco di carico può causare il timeout del controllo e impostare la destinazione come non disponibile, generando un falso allarme mentre gli utenti reali continuano a essere serviti.
Esegui quindi Uptime Kuma su un VPS diverso da quello che monitora, preferibilmente presso un provider o in una regione differenti, e raggiungi i servizi come fanno gli utenti: tramite Internet pubblico e usando il nome host. È sufficiente un'istanza economica e un singolo VPS di monitoraggio di piccole dimensioni può controllare tutti i server. Questa separazione è particolarmente importante per le applicazioni che ospiti, perché un servizio come una libreria fotografica PhotoPrism o Immich può saturare la CPU per ore durante l'indicizzazione di una nuova importazione, mentre un monitor che condivide lo stesso hardware segnalerebbe come non disponibile un servizio che è soltanto occupato. Per rilevare anche l'arresto di Kuma, aggiungi un heartbeat push da un cron eseguito altrove.
Prerequisiti e dimensionamento
- Un VPS Ubuntu 24.04 appena installato, con Docker Engine e il plugin Compose v2 installati dal repository apt ufficiale di Docker, non dal pacchetto
docker.iodella distribuzione, che è meno aggiornato. - 256 MB di RAM sono sufficienti per alcuni monitor. Da 512 MB a 1 GB offrono un margine adeguato per decine di monitor e il reverse proxy. Tra un controllo e l’altro, l’utilizzo della CPU è quasi nullo.
- Un dominio e un record DNS
A, ad esempiostatus.example.comche punti al VPS, solo se vuoi usare TLS e pubblicare una pagina di stato accessibile pubblicamente. Un’istanza privata può non usare il DNS e affidarsi a una VPN o a un tunnel SSH. - Connettività di rete in uscita verso le destinazioni degli avvisi: SMTP verso il provider di posta oppure HTTPS verso Telegram e Discord.
Il file Compose
Inseriscilo in /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:Avvia lo stack e monitora il primo avvio:
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 avvio corretto registra Listening on 3001 e poi non produce più output. Nel file ci sono tre scelte deliberate.
127.0.0.1:3001:3001, non 3001:3001. Docker pubblica le porte usando regole DNAT valutate prima che ufw possa vedere il pacchetto. Di conseguenza, un semplice 3001:3001 espone la dashboard su Internet indipendentemente dal firewall. Il binding sull'interfaccia di loopback la mantiene privata e lascia esposto solo il reverse proxy; un'istanza privata può evitare il proxy e raggiungere 3001 tramite una VPN WireGuard self-hosted.
Un volume denominato in /app/data. Tutto ciò che Uptime Kuma conserva, cioè il database SQLite, i monitor, le impostazioni delle notifiche e i logo delle pagine di stato, si trova lì. Se lo perdi, riparti da una schermata di amministrazione vuota; è l'unico elemento di cui devi eseguire il backup.
L'immagine è fissata a un tag major, :2. Questa è la linea stabile attuale; controlla Docker Hub per verificare il major più recente prima di copiarlo e non usare mai un tag mobile come latest, che il progetto sta deprecando. Il passaggio a una nuova versione major di questa immagine esegue una migrazione irreversibile del database. Devi avviarlo deliberatamente, non ritrovartici durante un normale pull.
Una precisazione: /app/data deve trovarsi su un filesystem che supporti i file lock POSIX. Un volume Docker locale va bene; su NFS il database SQLite si corrompe e vengono visualizzati SQLITE_BUSY e database disk image is malformed. Non usare quindi una condivisione di rete.
Prima esecuzione: creazione dell'account amministratore
Accedi all'istanza tramite il proxy all'indirizzo https://status.example.com oppure tramite un tunnel SSH: esegui ssh -L 3001:127.0.0.1:3001 user@your-vps e apri http://localhost:3001. La prima pagina contiene il modulo di configurazione per il nome utente e la password dell'amministratore; non esiste alcun accesso predefinito. Scegli una password robusta: questa dashboard visualizza gli indirizzi interni e i token di tutti gli elementi monitorati. Se in seguito dimentichi la password, reimpostala dall'host, non dal browser:
sudo docker compose exec uptime-kuma npm run reset-passwordAggiungere prima i canali di notifica e testarli
Configura gli avvisi prima di aggiungere i monitor, così puoi associare un canale durante la creazione di ciascuno. Vai a Settings then Notifications then Setup Notification e usa il pulsante Test di ogni canale per verificare che il messaggio arrivi. Una notifica non testata è infatti il secondo motivo più comune per cui una configurazione non funziona senza produrre errori visibili.
Email (SMTP). Inserisci host, porta, crittografia, nome utente, password, un indirizzo From e un indirizzo To. Le due combinazioni funzionanti sono 465 con "Secure" impostato su TLS/SSL oppure 587 con STARTTLS. Per Gmail e per la maggior parte dei provider con autenticazione a due fattori devi generare una app password; una normale password dell'account restituisce Error: Invalid login: 535-5.7.8 Username and Password not accepted.
Telegram. Invia un messaggio a @BotFather, invia /newbot e copia il token del bot. Per ottenere il tuo ID chat, invia prima un messaggio al nuovo bot, apri https://api.telegram.org/bot<token>/getUpdates e leggi chat.id dal JSON. Un bot a cui non hai mai inviato un messaggio ha getUpdates vuoto e non ha una destinazione a cui inviare notifiche.
Discord. Nel canale, apri Edit Channel then Integrations then Webhooks then New Webhook, copia l'URL e incollalo come notifica Discord.
Webhook generico. Per qualsiasi altro servizio, ad esempio un webhook in ingresso di Slack, un endpoint personalizzato o un hook per la domotica, il tipo Webhook invia un payload JSON tramite POST a un URL fornito da te. L'integrazione Apprise inclusa supporta inoltre la maggior parte degli altri circa novanta servizi presenti nell'elenco. Se preferisci evitare che un servizio di terze parti si interponga tra un'interruzione del servizio e il tuo telefono, scegli il tipo integrato ntfy e indirizzalo a un server ntfy gestito da te, che invia le notifiche al tuo dispositivo tramite un canale sotto il tuo completo controllo.
Aggiungere i monitor, un tipo alla volta
Fare clic su Add New Monitor, scegliere un tipo e impostare il Friendly Name, il Check Interval (60 secondi è un valore adeguato), i Retries (il numero di errori consecutivi prima di considerare il servizio «down»; usare 2 o 3 per evitare una notifica causata da un singolo pacchetto perso) e le notifiche da attivare. I tipi disponibili sono:
- HTTP(s). Un URL completo. Il servizio è considerato attivo quando restituisce un codice di stato accettato (200-299 per impostazione predefinita; ampliare l'intervallo in Accepted Status Codes se
301o401è normale nel tuo caso). È il controllo principale per siti web e API. - HTTP(s) - Keyword. Invia la stessa richiesta, ma considera il servizio attivo solo se nel corpo è presente una determinata stringa oppure, con Invert, se la stringa è assente. Questo rileva il caso in cui il sito restituisca
200 OKmentre visualizza «Error establishing a database connection», situazione che un semplice controllo HTTP considera corretta. È anche il controllo adatto per un frontend accessibile tramite browser che comunica con un backend separato, ad esempio una skin Halcyon per un videostore basata su Jellyfin, la cui struttura della pagina restituisce regolarmente200anche quando il media server sottostante non è raggiungibile. - TCP Port. Stabilisce una semplice connessione TCP verso un host e una porta, per i servizi che non usano HTTP: SSH sulla porta 22, Postgres sulla porta 5432, un server SMTP sulla porta 25 oppure un game server.
- Ping. Invia una richiesta echo ICMP per verificare la raggiungibilità e la latenza. Tuttavia, molte reti e firewall cloud scartano ICMP. Un monitor Ping in stato di errore può quindi indicare che l'host è inattivo oppure che il provider blocca il ping. Verificare con un monitor TCP.
- DNS. Risolve un record (A, AAAA, MX, TXT e così via) tramite un resolver specificato e può verificare la risposta. Questo permette di rilevare rapidamente un'interruzione presso il registrar o nel servizio DNS.
- Push. È il monitoraggio avviato dall'interno, descritto nella sezione successiva.
Monitorare un cron job con un monitor push (heartbeat)
Tutti i monitor precedenti raggiungono il servizio dall’esterno. Un monitor push funziona al contrario: Uptime Kuma attende e il job chiama il monitor per segnalare di essere stato eseguito. È l’unico metodo affidabile per monitorare un backup o un cron job: un controllo HTTP verifica che un URL risponda, ma solo il job sa se è stato completato.
Create un monitor di tipo Push. Uptime Kuma genera un URL univoco simile al seguente:
https://status.example.com/api/push/j8Xa2Kd9Qe?status=up&msg=OK&ping=Impostate Heartbeat Interval sull’intervallo di esecuzione del job, aggiungendo un piccolo margine. Quindi aggiungete una riga alla fine dello script, in modo che venga eseguita solo in caso di successo:
#!/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="Se il job non riesce, set -e si interrompe prima di eseguire curl; se il server è inattivo, il job non viene eseguito. In entrambi i casi l’heartbeat si interrompe e, allo scadere dell’intervallo più la finestra dei tentativi, Uptime Kuma imposta il monitor sullo stato down e invia un avviso. Trattate il token push come un secret: chiunque ne disponga può falsificare un heartbeat valido.
Crea una pagina di stato pubblica
Una pagina di stato è la vista destinata ai clienti: mostra quali servizi sono operativi e il relativo storico recente, senza esporre la dashboard. Vai in Status Pages, quindi New Status Page, assegna un nome e uno slug (il percorso pubblico, ad esempio /status/main), trascina i monitor che vuoi inserire in gruppi come "Websites" e "APIs", aggiungi un logo e una breve descrizione, quindi fai clic su Save. Puoi anche associare la pagina a un dominio dedicato, in modo che status.example.com la renda disponibile direttamente.
Tieni presenti due aspetti: aggiungi soltanto i monitor che sei disposto a rendere pubblici, perché una pagina di stato rivela che un servizio esiste e se è operativo; inoltre, la dashboard resta protetta dal login, mentre la pagina di stato è intenzionalmente pubblica e non richiede autenticazione.
Impostarlo dietro un reverse proxy con TLS e gestire correttamente i WebSocket
Per un'istanza pubblica, configura un reverse proxy davanti al container associato al loopback per gestire TLS e un hostname. Il dettaglio che causa più problemi è questo: l'interfaccia di Uptime Kuma è un'applicazione Socket.IO in tempo reale, quindi il proxy deve aggiornare la connessione WebSocket. Se manca questa configurazione, la pagina viene caricata ma non stabilisce mai la connessione; il dashboard resta su "Connecting...", gli heartbeat in tempo reale non si aggiornano e la console del browser mostra WebSocket connection to 'wss://.../socket.io/...' failed.
Installa nginx e certbot, quindi scrivi il vhost che inoltra le richieste alla porta di loopback. Per il momento configurarlo sulla porta 80 e lascia che sia certbot ad aggiungere TLS in seguito; la challenge, il timer di rinnovo e le relative modalità di errore sono descritti in emissione di certificati Let's Encrypt con certbot e nginx.
sudo apt install -y nginx certbot python3-certbot-nginxSalva il file come /etc/nginx/sites-available/status.example.com; le due righe relative ai WebSocket sono quelle importanti:
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;
}
}Abilita il sito, verifica la configurazione, quindi lascia che certbot riscriva il blocco per l'ascolto sulla porta 443, inserisca il certificato e aggiunga un redirect da HTTP a 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 coppia Upgrade e Connection "upgrade" è essenziale, mentre proxy_read_timeout 3600s impedisce a nginx di chiudere la connessione persistente; certbot copia entrambe nel blocco 443 che genera. Se esegui già diversi container dietro un unico proxy, instradarli tramite Traefik con TLS automatico offre lo stesso risultato usando le label dei container e inoltra gli aggiornamenti WebSocket per impostazione predefinita.
Non proteggere l'intero vhost con l'autenticazione di base, perché in questo modo bloccheresti anche la pagina di stato pubblica e l'endpoint /api/push. Mantieni il login integrato di Uptime Kuma, aggiungi fail2ban per monitorare i tentativi di accesso non riusciti ripetuti se l'istanza è esposta a Internet e, se il dashboard non deve mai essere pubblico, rimuovi il proxy e accedi tramite una VPN.
Monitoraggio delle scadenze dei certificati, configurato correttamente
Un monitor HTTP(s) può avvisarti anche prima della scadenza di un certificato TLS: attiva Certificate Expiry Notification e Uptime Kuma invierà un avviso con il numero di giorni di anticipo configurato. Due errori possono causare una lettura errata. Configura il monitor usando il nome host, non l'indirizzo IP: in assenza di SNI, la richiesta riceve il certificato predefinito del server e viene visualizzato Hostname/IP does not match certificate's altnames. Inoltre, non attivare Ignore TLS/SSL Error per un monitor da cui vuoi ricevere avvisi sulle scadenze: questa opzione serve per gli host interni con certificati autofirmati (unable to verify the first certificate, DEPTH_ZERO_SELF_SIGNED_CERT), ma impedisce a Uptime Kuma di verificare completamente il certificato, inclusa la scadenza.
Backup: una sola directory
Poiché tutto risiede in /app/data, il backup consiste in una copia di quel volume eseguita quando il container è arrestato. In questo modo il file SQLite resta coerente:
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 startPrima verifica il nome effettivo del volume con docker volume ls | grep kuma, perché Compose lo antepone al nome della directory del progetto. Poi copia l'archivio tar fuori dal server: un backup conservato sullo stesso VPS è una copia, non un backup. Il ripristino segue la procedura inversa: arresta lo stack, estrai i file in un volume /app/data vuoto e avvialo.
Aggiornamenti
Gli aggiornamenti consistono nel scaricare una nuova immagine:
cd /srv/uptime-kuma
sudo docker compose pull
sudo docker compose up -dIl nuovo container esegue eventuali migrazioni del database al primo avvio; monitorare docker compose logs -f. Eseguire il backup indicato sopra prima del pull e mantenere la stessa versione principale: il passaggio da :1 a :2 è una migrazione a senso unico, quindi eseguire prima il backup e consultare le note di rilascio.
Modalità di errore e stringhe visualizzate
Falso stato "down" in un monitor configurato su localhost. Il monitor diventa rosso con timeout of 48000ms exceeded o connect ETIMEDOUT, ma il servizio risponde dal laptop. Se il monitor punta allo stesso host su cui è in esecuzione Uptime Kuma, un picco di CPU o memoria ha impedito l'esecuzione del controllo; il problema non riguarda il target. Sposta il monitor su un VPS separato e impostalo sul nome host pubblico.
connect ECONNREFUSED 127.0.0.1:443 (o qualsiasi altra porta). Su quella porta non era in ascolto alcun servizio: il servizio è inattivo oppure hai monitorato localhost dall'interno del container, dove 127.0.0.1 indica il container, non il server. Monitora il nome host pubblico, non il loopback.
Invalid login: 535-5.7.8 Username and Password not accepted durante un test email. Le credenziali SMTP sono errate oppure il provider richiede una password specifica per l'applicazione, ma è stata usata la password dell'account. Genera una password per l'applicazione e inseriscila.
connect ETIMEDOUT o queryA ETIMEDOUT <host> durante un test email. La porta è errata oppure il provider blocca il traffico SMTP in uscita. Verifica che 465 o 587 corrisponda all'impostazione Secure/STARTTLS e testa la connessione dall'host con nc -vz smtp.example.com 587. Molti provider bloccano 25 in uscita e alcuni bloccano le porte di submission finché non ne richiedi lo sblocco.
self signed certificate o unable to verify the first certificate durante un test email. Il server SMTP presenta un certificato che Node non considera attendibile; correggi il certificato del server di posta invece di aggirare il problema.
Dashboard bloccata su "Connecting...", nella console compare WebSocket connection ... failed. Il reverse proxy non aggiorna la connessione WebSocket. In nginx aggiungi gli header Upgrade e Connection "upgrade" oppure usa un proxy che li inoltra per impostazione predefinita, come Traefik o Caddy. L'HTML viene caricato perché si tratta di una normale richiesta HTTP GET; solo il socket in tempo reale richiede l'aggiornamento.
Il monitor della scadenza del certificato non segnala mai l'avviso oppure segnala una scadenza errata. Potrebbe essere selezionata l'opzione Ignore TLS/SSL Error, che disabilita il controllo del certificato, oppure il monitor potrebbe puntare a un IP e leggere il certificato errato perché manca SNI, mostrando Hostname/IP does not match certificate's altnames. Deseleziona l'opzione di ignorare gli errori e monitora il servizio tramite nome host.
SQLITE_BUSY o database disk image is malformed nei log. Il volume /app/data si trova su un filesystem senza un corretto supporto per il file locking, di solito NFS; spostalo su un volume Docker locale e ripristina i dati dal backup.
FAQ
Dove devo eseguire il monitoraggio dell'uptime?
Su un server diverso da quelli che monitora, idealmente presso un altro provider o in un'altra regione, raggiungendoli tramite hostname sulla rete Internet pubblica, proprio come farebbero gli utenti. Se il monitor condivide il server con i sistemi monitorati, l'interruzione che rende indisponibile il server rende indisponibile anche il monitor. Inoltre, un host sovraccarico può segnalare come "down" servizi che invece funzionano correttamente. Un VPS separato e di piccole dimensioni evita entrambi i problemi.
Come posso ricevere avvisi su Telegram o tramite email?
Aggiungi il canale in Settings then Notifications, quindi assegnalo a ciascun monitor. Per Telegram, crea un bot con @BotFather e leggi chat.id da https://api.telegram.org/bot<token>/getUpdates; per l'email, usa 465 per SSL o 587 per STARTTLS, con una password per le app se il provider usa l'autenticazione a due fattori. Premi Test e verifica che il messaggio arrivi prima di fare affidamento sul canale.
Uptime Kuma può monitorare un cron job o uno script di backup?
Sì. Questo è il monitor Push: Uptime Kuma fornisce un URL e devi curl alla fine dello script, in modo che la notifica venga inviata solo in caso di esecuzione corretta. Se il job non riesce o il server è indisponibile, l'heartbeat non arriva e ricevi un avviso allo scadere dell'intervallo. È l'unico metodo affidabile per verificare che un job pianificato sia stato realmente eseguito, perché un controllo esterno non può vedere cosa accade al suo interno.
Uptime Kuma o Zabbix: quale dovrei usare?
Uptime Kuma risponde alla domanda "è attivo dall'esterno e mi ha inviato un avviso?" in dieci minuti, con un consumo minimo di risorse e una pagina di stato. Non raccoglie metriche dettagliate, come l'andamento di CPU, memoria e disco, né applica soglie sull'intera flotta. Per questi casi, un server di monitoraggio Zabbix completo è lo strumento più completo basato su agenti e molti usano entrambe le soluzioni. Non hai ancora deciso cosa eseguire? la nostra panoramica di ciò che puoi ospitare autonomamente nel 2026 inserisce il monitoraggio nel contesto.