Il y a quelques mois, Nextcloud a arrêté de synchroniser pour toute la famille pendant 48h. Personne ne l’a remarqué immédiatement – le service ne tombait pas, il se connectait, mais les uploads échouaient silencieusement. Ce n’est que quand ma femme a voulu retrouver des photos d’un week-end qu’on a découvert qu’elles n’avaient jamais été sauvegardées.
La cause : le disque de données Nextcloud était plein. 0 octet disponible. Pas d’alerte, pas de log visible depuis l’interface. Juste un service qui tombait en silence.
Depuis, j’ai une stack monitoring complète sur hubble.arewel.com, un VPS OVH dédié dont le seul travail est de surveiller le homelab depuis l’extérieur – une panne réseau ou électrique chez moi n’emporte pas le monitoring avec elle. Ce guide documente comment je l’ai mise en place.
Architecture
heighliner (192.168.1.10, à la maison)
├── pve-exporter :9221 ← métriques Proxmox (VMs, LXC, stockage, nœud)
│
LXC 150 Pi-hole → node_exporter :9100
LXC 162 Nextcloud → node_exporter :9100
LXC 163 Gitea → node_exporter :9100
LXC 164 Jellyfin → node_exporter :9100
VM 300 HAOS → node_exporter :9100 (via Prometheus integration HAOS)
...
│
│ (tunnel VPN entre hubble et le réseau homelab)
▼
hubble.arewel.com (VPS OVH)
└── Prometheus
└── Grafana
└── AlertManager → Telegram
Le tunnel entre hubble et le réseau homelab est ce qui permet à Prometheus de scraper des cibles sur des IPs privées (192.168.x.x) depuis un VPS public – sans lui, aucune de ces cibles n’est joignable depuis l’extérieur. Prometheus scrape toutes les sources toutes les 30 secondes. Grafana interroge Prometheus. AlertManager évalue les règles d’alerte et pousse vers Telegram.
Déployer Prometheus
Sur hubble (VPS OVH, Debian), installer Prometheus :
PROMETHEUS_VERSION="2.52.0"
cd /tmp
wget -q https://github.com/prometheus/prometheus/releases/download/v${PROMETHEUS_VERSION}/prometheus-${PROMETHEUS_VERSION}.linux-amd64.tar.gz
tar xzf prometheus-${PROMETHEUS_VERSION}.linux-amd64.tar.gz
install -m 755 prometheus-${PROMETHEUS_VERSION}.linux-amd64/prometheus /usr/local/bin/
install -m 755 prometheus-${PROMETHEUS_VERSION}.linux-amd64/promtool /usr/local/bin/
useradd --no-create-home --shell /bin/false prometheus
mkdir -p /etc/prometheus /var/lib/prometheus
chown prometheus:prometheus /var/lib/prometheus
Créer la configuration :
# /etc/prometheus/prometheus.yml
global:
scrape_interval: 30s
evaluation_interval: 30s
external_labels:
homelab: 'ms01'
rule_files:
- /etc/prometheus/rules/*.yml
alerting:
alertmanagers:
- static_configs:
- targets: ['localhost:9093']
scrape_configs:
- job_name: 'proxmox-node'
static_configs:
- targets: ['192.168.1.10:9221']
metrics_path: /pve
params:
target: ['localhost']
module: ['default']
- job_name: 'nodes'
static_configs:
- targets:
- '192.168.1.150:9100' # Pi-hole LXC
- '192.168.30.160:9100' # NPM
- '192.168.30.161:9100' # Vaultwarden
- '192.168.30.162:9100' # Nextcloud
- '192.168.30.163:9100' # Gitea
- '192.168.30.164:9100' # Jellyfin
- '192.168.30.165:9100' # Immich
- '192.168.30.166:9100' # Paperless
- '192.168.30.50:9100' # PBS VM
- job_name: 'pihole'
static_configs:
- targets: ['192.168.1.150:9617'] # pihole-exporter
Créer le service systemd :
cat > /etc/systemd/system/prometheus.service << 'EOF'
[Unit]
Description=Prometheus
After=network.target
[Service]
User=prometheus
ExecStart=/usr/local/bin/prometheus \
--config.file=/etc/prometheus/prometheus.yml \
--storage.tsdb.path=/var/lib/prometheus \
--storage.tsdb.retention.time=30d \
--web.listen-address=:9090
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
systemctl enable --now prometheus
Prometheus est accessible sur http://localhost:9090 sur hubble (à réserver derrière le VPN, jamais exposé publiquement tel quel). Vérifier que les targets sont UP dans Status > Targets.
Installer node_exporter sur chaque machine
node_exporter est un binaire unique qui expose les métriques système (CPU, RAM, disque, réseau) sur le port 9100. Il doit tourner sur chaque VM et LXC à monitorer.
Script d’installation à appliquer dans chaque container :
NODE_EXPORTER_VERSION="1.9.0"
cd /tmp
wget -q https://github.com/prometheus/node_exporter/releases/download/v${NODE_EXPORTER_VERSION}/node_exporter-${NODE_EXPORTER_VERSION}.linux-amd64.tar.gz
tar xzf node_exporter-${NODE_EXPORTER_VERSION}.linux-amd64.tar.gz
install -m 755 node_exporter-${NODE_EXPORTER_VERSION}.linux-amd64/node_exporter /usr/local/bin/
useradd --no-create-home --shell /bin/false node_exporter
cat > /etc/systemd/system/node_exporter.service << 'EOF'
[Unit]
Description=Node Exporter
After=network.target
[Service]
User=node_exporter
ExecStart=/usr/local/bin/node_exporter \
--web.listen-address=:9100 \
--collector.filesystem.mount-points-exclude='^/(dev|proc|run|sys|tmp)($|/)'
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
systemctl enable --now node_exporter
Pour l’installer sur plusieurs machines rapidement, j’utilise un one-liner depuis le nœud Proxmox :
for CT in 150 160 161 162 163 164 165 166 167; do
echo "=== LXC $CT ==="
pct exec $CT -- bash -c "$(cat /root/install-node-exporter.sh)"
done
pve-exporter : métriques Proxmox natives
pve-exporter interroge l’API Proxmox et expose les métriques des VMs, LXC, nœuds et stockages. Il tourne directement sur le nœud Proxmox.
# Sur le nœud Proxmox (pas dans un LXC)
apt install -y python3-pip python3-venv
python3 -m venv /opt/pve-exporter
/opt/pve-exporter/bin/pip install prometheus-pve-exporter
Créer l’utilisateur Proxmox dédié avec droits minimaux :
pveum user add pve-exporter@pve --comment "Prometheus PVE exporter"
pveum role add PVEExporter --privs "Datastore.Audit VM.Audit Pool.Audit Sys.Audit"
pveum acl modify / --user pve-exporter@pve --role PVEExporter
# Créer un token API (plus sûr qu'un mot de passe)
pveum user token add pve-exporter@pve prometheus --privsep 0
# Copier le token value affiché - il ne sera montré qu'une fois
Configuration pve-exporter :
# /etc/pve-exporter/pve.yml
default:
user: pve-exporter@pve
token_name: prometheus
token_value: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
verify_ssl: false
cat > /etc/systemd/system/pve-exporter.service << 'EOF'
[Unit]
Description=PVE Prometheus Exporter
After=network.target
[Service]
ExecStart=/opt/pve-exporter/bin/pve_exporter /etc/pve-exporter/pve.yml 9221 0.0.0.0
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
EOF
systemctl enable --now pve-exporter
Vérifier que les métriques Proxmox sont disponibles :
curl -s http://localhost:9221/pve?target=localhost | grep pve_version
# pve_version_info{release="8.4",repoid="...",version="8.4.18"} 1
Déployer Grafana
Grafana s’installe à côté de Prometheus sur hubble :
apt install -y apt-transport-https software-properties-common
wget -q -O /usr/share/keyrings/grafana.key https://apt.grafana.com/gpg.key
echo "deb [signed-by=/usr/share/keyrings/grafana.key] https://apt.grafana.com stable main" \
> /etc/apt/sources.list.d/grafana.list
apt update && apt install -y grafana
systemctl enable --now grafana-server
Grafana sur http://localhost:3000 (derrière le VPN, jamais exposé publiquement tel quel). Login : admin/admin, changer immédiatement.
Ajouter Prometheus comme datasource : Configuration > Data sources > Add > Prometheus > URL: http://localhost:9090.
Dashboards recommandés (import par ID depuis grafana.com) :
| Dashboard | ID | Usage |
|---|---|---|
| Node Exporter Full | 1860 | Métriques système de chaque machine |
| Proxmox VE | 10347 | Nœud Proxmox : VMs, LXC, stockage, réseau |
| ZFS | 7845 | Pools ZFS : santé, ARC, I/O |
| Pi-hole | 14559 | Requêtes DNS, domaines bloqués, clients |
AlertManager et alertes Telegram
AlertManager évalue les règles Prometheus et envoie des notifications. L’intégration Telegram via un bot est la plus simple à configurer.
Créer un bot Telegram : parler à @BotFather sur Telegram, /newbot, noter le token. Récupérer le chat ID : envoyer un message au bot, puis curl https://api.telegram.org/bot<TOKEN>/getUpdates.
Installer AlertManager sur hubble, à côté de Prometheus :
ALERTMANAGER_VERSION="0.27.0"
cd /tmp
wget -q https://github.com/prometheus/alertmanager/releases/download/v${ALERTMANAGER_VERSION}/alertmanager-${ALERTMANAGER_VERSION}.linux-amd64.tar.gz
tar xzf alertmanager-${ALERTMANAGER_VERSION}.linux-amd64.tar.gz
install -m 755 alertmanager-${ALERTMANAGER_VERSION}.linux-amd64/alertmanager /usr/local/bin/
Configuration AlertManager :
# /etc/alertmanager/alertmanager.yml
global:
resolve_timeout: 5m
route:
group_by: ['alertname', 'job']
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
receiver: telegram
receivers:
- name: telegram
telegram_configs:
- bot_token: '<BOT_TOKEN>'
chat_id: <CHAT_ID>
message: |
{{ range .Alerts }}
*{{ .Status | toUpper }}* {{ .Labels.alertname }}
{{ .Annotations.summary }}
{{ if .Labels.instance }}Instance: {{ .Labels.instance }}{{ end }}
{{ end }}
parse_mode: Markdown
Règles d’alerte critiques pour un homelab :
# /etc/prometheus/rules/homelab.yml
groups:
- name: homelab-critical
rules:
- alert: InstanceDown
expr: up == 0
for: 2m
labels:
severity: critical
annotations:
summary: "{{ $labels.instance }} injoignable depuis 2 minutes"
- alert: DiskSpaceCritical
expr: (node_filesystem_avail_bytes{fstype!="tmpfs"} / node_filesystem_size_bytes) < 0.10
for: 5m
labels:
severity: critical
annotations:
summary: "Disque {{ $labels.mountpoint }} sur {{ $labels.instance }} à moins de 10%"
- alert: HighMemoryUsage
expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) > 0.90
for: 10m
labels:
severity: warning
annotations:
summary: "{{ $labels.instance }} : utilisation RAM > 90%"
- alert: ZFSPoolDegraded
expr: node_zfs_zpool_state{state!="online"} == 1
for: 1m
labels:
severity: critical
annotations:
summary: "Pool ZFS {{ $labels.pool }} en état {{ $labels.state }}"
La règle DiskSpaceCritical aurait détecté le problème Nextcloud de l’introduction – 10% libre sur un volume de 50 Go, ça déclenche l’alerte à 5 Go restants. Largement le temps de réagir.
Ce qui a coincé
Le premier déploiement, pve-exporter retournait des métriques vides pour les VMs arrêtées. Dans Grafana, les panneaux “VMs running” affichaient 0 même quand des machines tournaient.
Le problème : pve-exporter était configuré avec un utilisateur pve-exporter@pve qui avait les droits sur / mais dans Proxmox, les droits sur / ne s’appliquent pas automatiquement aux pools ou ressources créées après l’attribution des droits.
Diagnostic avec promtool :
curl -s "http://192.168.1.10:9221/pve?target=localhost" | grep -c "pve_guest_info"
# 0 ← devrait être le nombre de VMs
Correction : reconfigurer les ACL avec la propagation activée :
pveum acl modify / --user pve-exporter@pve --role PVEExporter --propagate 1
Après redémarrage du service, toutes les VMs et LXC apparaissaient dans les métriques.
Deuxième incident : après une mise à jour du paquet prometheus-pve-exporter via pip, le format des métriques a changé. Les queries PromQL dans Grafana utilisaient pve_cpu_usage_ratio mais le nouveau format l’avait renommé pve_cpu_usage_percent. Tous les panneaux CPU dans le dashboard Proxmox affichaient “No data”.
Correction : éditer chaque panel dans Grafana pour adapter le nom de métrique, ou revenir à la version antérieure du paquet. Depuis, je pin la version dans le service systemd :
/opt/pve-exporter/bin/pip install prometheus-pve-exporter==1.3.1
Et je documente la version dans /etc/pve-exporter/VERSION avant chaque mise à jour.