Général 8 min de lecture · 1 665 mots

Monitorer son homelab Proxmox avec Grafana, Prometheus et node_exporter

Monitorer son homelab Proxmox avec Grafana, Prometheus et node_exporter
Général · 2026.06.09

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.

Une remarque, un retour ?

Cet article est vivant - corrections, contre-arguments et retours de production sont les bienvenus. Trois canaux, choisissez celui qui vous convient.

Laisser un commentaire