Monitoring mit Grafana & Prometheus: dein Server in Live-Dashboards
Ein vollständiger Monitoring-Stack aus Prometheus, node-exporter, cAdvisor und Grafana hinter Traefik – mit Live-Metriken zu Host und Containern.
Inhaltsverzeichnis
Uptime Kuma sagt dir, ob ein Dienst läuft. Dieser Stack sagt dir, wie es ihm geht: CPU, RAM, Festplatte, Netzwerk und die Last jedes einzelnen Containers – als Live-Dashboards mit Verlauf. Wir bauen die Kombination, die im Selfhosting zum De-facto-Standard geworden ist: Prometheus sammelt, Grafana zeigt.
Was bauen wir?
Am Ende läuft ein kompletter Monitoring-Stack hinter deinem Traefik-Proxy, erreichbar
unter https://grafana.DEINE_DOMAIN mit gültigem HTTPS. Vier Bausteine greifen ineinander:
- Prometheus 3.13 – die Zeitreihen-Datenbank. Sie holt (scrapt) im Intervall Metriken von den anderen Diensten und speichert sie mit Zeitstempel.
- node-exporter 1.12 – liefert die Host-Metriken: CPU, Arbeitsspeicher, Load, Festplatten, Netzwerk-Interfaces.
- cAdvisor 0.55 – liefert die Container-Metriken: CPU und RAM pro laufendem Container, damit du siehst, welcher Dienst frisst.
- Grafana 13.1 – die Oberfläche: fertige Dashboards, die aus den Prometheus-Daten Graphen und Anzeigen machen.
Der entscheidende Unterschied zu Uptime Kuma: Statt „grün/rot" bekommst du Trends – du erkennst, dass der RAM seit Tagen langsam vollläuft, bevor der Server swappt. Nur Grafana ist von außen erreichbar; Prometheus, node-exporter und cAdvisor bekommen keine öffentliche Route.
Wie groß muss der Server sein?
VPS 2000 G12
8 vCore · 16 GB RAM · 512 GB NVMe
ab 19,24 €/Monat
Genug RAM und Speicher, um Monitoring neben deinen Apps mitlaufen zu lassen.
💶 5 € Gutschein für netcup-Neukunden:
36nc17844976032
(nur Neukunden, keine Domains)
Voraussetzungen
- Sicherheit im Umgang mit Docker Compose – Services, Volumes, Netzwerke. Falls das noch neu ist: Docker Compose verstehen.
- Ein laufender Traefik-Reverse-Proxy mit dem gemeinsamen
proxy-Netzwerk und dem Let’s-Encrypt-Resolverle– wie in Reverse Proxy mit Traefik aufgebaut. - Eine Subdomain
grafana.DEINE_DOMAIN, deren DNS-Record (A/AAAA) auf deine Server-IP zeigt – siehe Domain mit Server verbinden.
Schritt für Schritt
Schritt 1: Die Architektur verstehen
Bevor wir Dateien anlegen, das Bild dahinter – sonst debuggst du blind. Prometheus
arbeitet nach dem Pull-Prinzip: Nicht die Dienste schicken ihre Werte irgendwohin,
sondern Prometheus fragt sie aktiv ab. Jeder „Exporter" stellt seine Metriken unter
/metrics bereit, Prometheus holt sie alle 15 Sekunden ab und legt sie in seiner
Zeitreihen-Datenbank ab. Grafana wiederum fragt Prometheus ab und zeichnet daraus die
Graphen.
Warum überhaupt Pull statt Push? Weil Prometheus so jederzeit weiß, ob ein Ziel lebt:
Antwortet ein Exporter nicht, ist das selbst eine Information (up = 0). Du brauchst
keine Agenten, die aktiv „nach Hause telefonieren", und kannst jedes neue Ziel einfach in
die Konfiguration eintragen. Ein Exporter ist dabei nichts Magisches – er ist ein winziger
Webserver, der unter /metrics eine Textliste aktueller Messwerte ausliefert.
Daraus folgt die Sicherheitsregel dieses Setups: node-exporter, cAdvisor und Prometheus
haben keine Authentifizierung. Sie dürfen deshalb niemals offen im Internet
stehen. Prometheus und cAdvisor binden wir nur ins interne Docker-Netz monitoring;
node-exporter läuft im Host-Netz (warum, erklärt Schritt 4), bleibt aber dank
UFW und
netcup-Firewall von außen unerreichbar.
Ausschließlich Grafana hängt zusätzlich im proxy-Netz und bekommt eine Traefik-Route –
der einzige Dienst mit Login und öffentlicher Adresse.
Schritt 2: Projektordner und Prometheus konfigurieren
Leg die Ordnerstruktur an:
mkdir -p ~/monitoring/prometheus ~/monitoring/grafana/provisioning/datasources
cd ~/monitoringErstelle die Prometheus-Konfiguration. Sie legt fest, wen Prometheus in welchem Intervall abfragt:
# prometheus/prometheus.yml
global:
scrape_interval: 15s
scrape_timeout: 10s
scrape_configs:
- job_name: prometheus
static_configs:
- targets: ["localhost:9090"]
- job_name: node
static_configs:
- targets: ["host.docker.internal:9100"]
- job_name: cadvisor
static_configs:
- targets: ["cadvisor:8080"]Wichtig: cadvisor:8080 steht als Servicename da – Docker löst ihn im gemeinsamen
monitoring-Netz automatisch auf. Der node-exporter läuft dagegen im Host-Netz
(Schritt 4 erklärt, warum) und wird deshalb über host.docker.internal:9100
angesprochen – diesen Namen bekommt Prometheus gleich per extra_hosts auf die
IP des Docker-Hosts gemappt. scrape_interval: 15s ist ein guter Kompromiss – oft
genug für aussagekräftige Kurven, selten genug, um kaum Last zu erzeugen.
Schritt 3: Grafana-Datenquelle automatisch provisionieren
Statt die Prometheus-Datenquelle später von Hand in Grafana anzuklicken, richten wir sie per Provisioning ein – als Datei. Das ist reproduzierbar: Nach einem Neuaufbau ist alles sofort wieder da, ohne Klickarbeit.
# grafana/provisioning/datasources/datasource.yml
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true
editable: falseaccess: proxy bedeutet, dass Grafana selbst (serverseitig) Prometheus abfragt – der
Browser des Nutzers spricht nie direkt mit Prometheus. Genau deshalb kann Prometheus
intern bleiben.
Schritt 4: Der Compose-Stack
Jetzt das Herzstück. Erstelle die compose.yaml. Ersetze grafana.DEINE_DOMAIN und das
Grafana-Passwort:
services:
prometheus:
image: prom/prometheus:v3.13.1
container_name: prometheus
command:
- "--config.file=/etc/prometheus/prometheus.yml"
- "--storage.tsdb.path=/prometheus"
- "--storage.tsdb.retention.time=30d"
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prom_data:/prometheus
extra_hosts:
- "host.docker.internal:host-gateway"
networks: [monitoring]
restart: unless-stopped
node-exporter:
image: prom/node-exporter:v1.12.1
container_name: node-exporter
command:
- "--path.rootfs=/host"
network_mode: host
pid: host
volumes:
- "/:/host:ro,rslave"
restart: unless-stopped
cadvisor:
image: gcr.io/cadvisor/cadvisor:v0.55.1
container_name: cadvisor
privileged: true
devices:
- /dev/kmsg
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
- /dev/disk/:/dev/disk:ro
networks: [monitoring]
restart: unless-stopped
grafana:
image: grafana/grafana:13.1.0
container_name: grafana
depends_on: [prometheus]
environment:
GF_SECURITY_ADMIN_USER: admin
GF_SECURITY_ADMIN_PASSWORD: DEIN_GRAFANA_PASSWORT
GF_SERVER_ROOT_URL: https://grafana.DEINE_DOMAIN
GF_USERS_ALLOW_SIGN_UP: "false"
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning:ro
labels:
- "traefik.enable=true"
- "traefik.http.routers.grafana.rule=Host(`grafana.DEINE_DOMAIN`)"
- "traefik.http.routers.grafana.entrypoints=websecure"
- "traefik.http.routers.grafana.tls.certresolver=le"
- "traefik.http.services.grafana.loadbalancer.server.port=3000"
networks: [monitoring, proxy]
restart: unless-stopped
volumes:
prom_data:
grafana_data:
networks:
monitoring:
proxy:
external: trueWas hier wichtig ist:
--storage.tsdb.retention.time=30dhält Metriken 30 Tage vor. Mehr = mehr Aussagekraft, aber auch mehr Speicher. 30 Tage sind ein guter Start.- node-exporter braucht das Host-Wurzelverzeichnis (
/:/host:ro,rslave),pid: hostundnetwork_mode: host, um echte Host-Werte statt Container-Werte zu lesen.--path.rootfs=/hostsagt ihm, wo der Host-Blick liegt (nur lesend,:ro). Das Host-Netz ist kein Nice-to-have:/proc/netist an den Netzwerk-Namespace gebunden – in einem normalen Bridge-Netz sähe node-exporter nur sein eigenes Container-Interface (praktisch nur den Scrape-Traffic), nicht die echten Host-Interfaces. Der Preis: node-exporter lauscht damit auf Port9100des Hosts – deine Firewall hält den Port von außen zu, und Prometheus erreicht ihn über denextra_hosts-Eintrag (host-gateway). - cAdvisor braucht
privileged: true, das Gerät/dev/kmsgund Lese-Mounts auf/sysund/var/lib/docker, um alle Container zu erkennen. Das ist für ein Container-Monitoring unvermeidlich – deshalb bleibt cAdvisor strikt intern. - Nur
grafanaträgt Traefik-Labels und hängt improxy-Netz. Der interne Port ist 3000.GF_SERVER_ROOT_URLmuss die öffentliche HTTPS-Adresse sein, sonst brechen Logins und Weiterleitungen hinter dem Proxy. GF_USERS_ALLOW_SIGN_UP: "false"verhindert, dass sich Fremde selbst ein Konto anlegen. Setz ein starkes Admin-Passwort – Grafana steht öffentlich.
Schritt 5: Starten und die Prometheus-Targets prüfen
Starte den kompletten Stack:
docker compose up -dBeim ersten Mal lädt Docker vier Images – das dauert einen Moment. Prüfe, dass alle Container laufen:
docker compose psAlle vier Dienste (node-exporter, cadvisor, prometheus, grafana) sollten
running sein. Jetzt der entscheidende Test: Sieht Prometheus seine Ziele? Da
Prometheus intern bleibt, fragen wir es aus einem kurzlebigen Container heraus, der im
selben Netz hängt (Compose nennt das Netz monitoring_monitoring – Projektordner plus
Netzname):
docker run --rm --network monitoring_monitoring curlimages/curl:8.21.0 \
-s http://prometheus:9090/api/v1/query?query=upIn der JSON-Antwort steht für jedes Ziel ein "value" mit "1" – das heißt „erreichbar".
Im Test lieferten alle drei Jobs (prometheus, node, cadvisor) eine 1 zurück. Eine
0 bedeutet, dass Prometheus das Ziel nicht scrapen kann – dann hilft der Abschnitt
„Wenn es nicht funktioniert".
Schritt 6: Grafana aufrufen und Dashboards importieren
Ruf https://grafana.DEINE_DOMAIN auf. Traefik holt beim ersten Zugriff das
Zertifikat (ein paar Sekunden). Melde dich mit admin und deinem Passwort aus der
compose.yaml an. Dank Provisioning ist die Prometheus-Datenquelle bereits verbunden –
du findest sie unter Connections → Data sources:

Jetzt die Dashboards. Statt Panels selbst zu bauen, importieren wir zwei erprobte aus der Grafana-Community. Geh auf Dashboards → New → Import, trag die ID ein und klick auf Load:

Importiere diese beiden:
- 1860 – „Node Exporter Full": das umfassende Host-Dashboard.
- 19792 – „cAdvisor Dashboard": Metriken pro Container.
Wähle beim Import jeweils die Prometheus-Datenquelle aus. Danach zeigt „Node Exporter Full" deinen Host in Echtzeit – CPU-Auslastung, belegter RAM, Festplatte, Netzwerk, Uptime:

Das cAdvisor-Dashboard bricht dieselbe Last auf einzelne Container herunter – hier siehst du sofort, welcher Dienst wie viel CPU und RAM zieht:

Dashboards auch provisionieren
grafana/provisioning/dashboards/ eine .yml plus die Dashboard-JSONs). Für den Einstieg
ist der Import über die ID schneller; wenn dein Setup steht, lohnt sich das Provisionieren,
damit nach einem Neuaufbau alle Dashboards automatisch wieder da sind.Schritt 7: Die wichtigsten Metriken richtig lesen
Ein Dashboard voller Kurven nützt wenig, wenn du nicht weißt, worauf du achten musst. Diese fünf Werte im „Node Exporter Full"-Dashboard verraten dir fast alles über die Gesundheit deines Servers:
- CPU Busy. Kurze Spitzen sind normal. Kritisch wird es, wenn die Auslastung dauerhaft hoch bleibt – dann ist der Server der Flaschenhals.
- Sys Load. Die Load sollte im Mittel unter der Anzahl deiner CPU-Kerne liegen (im Test: 4 Kerne → Load dauerhaft über 4 ist ein Warnzeichen für Überlast).
- RAM Used. Wichtig: Linux nutzt freien Speicher als Datei-Cache – „belegt" ist also nicht gleich „knapp". Erst wenn neben dem Cache wenig frei bleibt und SWAP Used steigt, wird der Server zäh. Genau diesen schleichenden Anstieg willst du früh sehen.
- Root FS Used. Der Klassiker unter den Serverausfällen ist die volle Festplatte. Der Verlauf zeigt dir die Füllrate – so erkennst du Tage im Voraus, wann es eng wird.
- Network Traffic. Ungewöhnliche Ausschläge, obwohl gerade nichts läuft, können auf ein Backup, ein Update oder – im schlechten Fall – auf unerwünschten Datenverkehr hindeuten.
Im cAdvisor-Dashboard ist die spannendste Frage: Welcher Container zieht die Last? Wenn dein Server plötzlich langsam wird, siehst du hier auf einen Blick, ob es an einer einzelnen App liegt – und musst nicht raten. Damit wird aus „irgendwas ist langsam" ein gezieltes „der Foto-Import in Container X frisst gerade die CPU".
Wenn es nicht funktioniert
Symptom: Ein Target steht in Prometheus auf up = 0 bzw. „DOWN".
Ursache & Lösung: Prometheus erreicht den Exporter nicht. Bei cadvisor prüfe, dass
er im selben monitoring-Netz läuft und Name und Port exakt stimmen (cadvisor:8080).
Beim node-Job prüfe den extra_hosts-Eintrag am Prometheus-Service und das Target
host.docker.internal:9100 – ohne beides findet Prometheus den node-exporter im
Host-Netz nicht. Nach Änderungen an der prometheus.yml den Prometheus-Container neu
starten: docker compose restart prometheus.
Symptom: Das cAdvisor-Dashboard zeigt teils „No data" oder cAdvisor startet nicht.
Ursache & Lösung: Fehlen die Mounts oder privileged: true, sieht cAdvisor die
Container nicht. Kontrolliere den cadvisor-Block (Mounts, devices: /dev/kmsg). Einzelne
„No data"-Panels sind normal – manche Metriken (etwa CPU-Drosselung) gibt es nur, wenn du
den Containern CPU-Limits gesetzt hast.
Symptom: Grafana lädt hinter Traefik nicht richtig – Login schlägt fehl oder das Layout ist kaputt.
Ursache & Lösung: Fast immer stimmt GF_SERVER_ROOT_URL nicht. Sie muss exakt die
öffentliche Adresse sein (https://grafana.DEINE_DOMAIN). Prüfe außerdem, dass die
Traefik-Label loadbalancer.server.port=3000 gesetzt ist – Grafana lauscht intern auf 3000.
Symptom: Grafana zeigt in den Panels „No data", obwohl die Targets up sind.
Ursache & Lösung: Meist der Zeitbereich. Ein frischer Stack hat noch keine Historie – stell oben rechts „Last 15 minutes" ein und warte ein paar Minuten, bis Daten aufgelaufen sind. Prüfe zudem, dass beim Dashboard-Import die Prometheus-Datenquelle gewählt war.
Symptom: Der Speicherplatz auf dem Server wächst stetig.
Ursache & Lösung: Das ist die Prometheus-TSDB. Reduziere --storage.tsdb.retention.time
(z. B. auf 15d) oder überwache weniger Ziele. Das prom_data-Volume wächst mit Anzahl
der Metriken × Vorhaltezeit.
Wartung & Backups
- Was du sichern musst. Wichtig ist das
grafana_data-Volume: Es enthält deine Nutzer, Einstellungen und selbst gebauten Dashboards. Sichere es verschlüsselt und off-site mit Restic. Die Prometheus-Metriken (prom_data) sind reine Messwerte – ein Verlust ist verschmerzbar, ein Backup optional. - Updates. Die Image-Tags sind gepinnt (
prometheus:v3.13.1,grafana:13.1.0…). Zum Aktualisieren die Tags hochsetzen, danndocker compose pull && docker compose up -d. Lies vorher die Release-Notes – vor allem Grafana ändert zwischen Hauptversionen gelegentlich Standards. cAdvisor beziehst du vongcr.io; dort istv0.55.1derzeit die aktuellste verfügbare Image-Version. - Sicherheit. Grafana steht öffentlich – vergib ein starkes Admin-Passwort und lass
GF_USERS_ALLOW_SIGN_UPauffalse. Prometheus und cAdvisor bleiben intern; exponiere sie nicht „mal eben" zum Debuggen, sie haben keine Authentifizierung. - Der nächste Schritt: Alarme. Ein Dashboard hilft nur, wenn du hineinschaust. Grafana kann bei Schwellwerten (RAM > 90 %, Platte fast voll) selbst Alarme auslösen und z. B. per Push melden. Damit wird aus dem hübschen Dashboard ein Frühwarnsystem – ein guter Anschluss an dein bestehendes Uptime Kuma.
- Ehrlich zum Aufwand: Der Stack läuft nach dem Aufsetzen weitgehend allein. Plane gelegentlich einen Blick auf die Vorhaltezeit und den Speicherverbrauch ein und zieh die Versionen etwa quartalsweise nach.
Feedback per E-Mail: feedback@serverkueche.de
Das könnte dir auch schmecken

Uptime Kuma installieren: Server-Monitoring hinter Traefik
Uptime Kuma hinter Traefik aufsetzen und deine Dienste überwachen: Monitore, Benachrichtigungen und eine Status-Seite – …

Jellyfin selbst hosten: dein eigener Medienserver hinter Traefik
Jellyfin mit Docker hinter Traefik aufsetzen: deine Filme, Serien und Musik streamen – mit HTTPS und der Mediathek auf …

Immich selbst hosten: dein privates Foto-Backup hinter Traefik
Immich mit Docker hinter Traefik aufsetzen: die selbst gehostete Alternative zu Google Fotos – mit automatischem …