<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Grafana – Serverküche</title><link>https://serverkueche.de/tags/grafana/</link><description>Grafana – Neueste Beiträge von Serverküche</description><generator>Hugo</generator><language>de-DE</language><managingEditor>feedback@serverkueche.de (Serverküche)</managingEditor><webMaster>feedback@serverkueche.de (Serverküche)</webMaster><copyright>2026 Serverküche</copyright><lastBuildDate>Tue, 21 Jul 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://serverkueche.de/tags/grafana/index.xml" rel="self" type="application/rss+xml"/><item><title>Monitoring mit Grafana &amp; Prometheus: dein Server in Live-Dashboards</title><link>https://serverkueche.de/tutorials/monitoring-grafana-prometheus/</link><pubDate>Tue, 21 Jul 2026 00:00:00 +0000</pubDate><author>feedback@serverkueche.de (Serverküche)</author><guid>https://serverkueche.de/tutorials/monitoring-grafana-prometheus/</guid><description>Ein vollständiger Monitoring-Stack aus Prometheus, node-exporter, cAdvisor und Grafana hinter Traefik – mit Live-Metriken zu Host und Containern.</description><content:encoded><![CDATA[<p><a href="/tutorials/uptime-kuma-monitoring/">Uptime Kuma</a> sagt dir, <em>ob</em> ein Dienst läuft. Dieser
Stack sagt dir, <em>wie</em> 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.</p>
<h2 id="was-bauen-wir">Was bauen wir?</h2>
<p>Am Ende läuft ein kompletter <strong>Monitoring-Stack</strong> hinter deinem Traefik-Proxy, erreichbar
unter <code>https://grafana.DEINE_DOMAIN</code> mit gültigem HTTPS. Vier Bausteine greifen ineinander:</p>
<ul>
<li><strong>Prometheus 3.13</strong> – die Zeitreihen-Datenbank. Sie <em>holt</em> (scrapt) im Intervall
Metriken von den anderen Diensten und speichert sie mit Zeitstempel.</li>
<li><strong>node-exporter 1.12</strong> – liefert die <strong>Host-Metriken</strong>: CPU, Arbeitsspeicher, Load,
Festplatten, Netzwerk-Interfaces.</li>
<li><strong>cAdvisor 0.55</strong> – liefert die <strong>Container-Metriken</strong>: CPU und RAM pro laufendem
Container, damit du siehst, welcher Dienst frisst.</li>
<li><strong>Grafana 13.1</strong> – die Oberfläche: fertige Dashboards, die aus den Prometheus-Daten
Graphen und Anzeigen machen.</li>
</ul>
<p>Der entscheidende Unterschied zu Uptime Kuma: Statt „grün/rot&quot; bekommst du <strong>Trends</strong> –
du erkennst, dass der RAM seit Tagen langsam vollläuft, <em>bevor</em> der Server swappt. Nur
Grafana ist von außen erreichbar; Prometheus, node-exporter und cAdvisor bekommen
keine öffentliche Route.</p>
<div class="not-prose my-6 rounded-lg border-l-4 p-4 border-herb-400 bg-herb-50 dark:border-herb-700 dark:bg-herb-900/20">
  <p class="mb-1 flex items-center gap-2 font-semibold text-slate-900 dark:text-white">
    <span aria-hidden="true">🧑‍🍳</span>Wie groß muss der Server sein?
  </p>
  <div class="prose-kitchen text-sm">Der Stack selbst ist genügsam – im Test belegte er nur ein paar Hundert MB RAM. Der
Speicherhunger kommt von der <strong>Vorhaltezeit</strong> (Retention) von Prometheus: Je länger du
Metriken behältst und je mehr Dienste du überwachst, desto mehr Platz und RAM braucht die
Zeitreihen-Datenbank. Weil Monitoring meist <em>zusätzlich</em> zu deinen eigentlichen Apps läuft,
empfehlen wir den VPS 2000 mit mehr Reserve – für ein reines Test-Setup reicht auch der
kleinere.</div>
</div>
<div class="not-prose my-6 overflow-hidden rounded-xl border border-paprika-200 bg-paprika-50 dark:border-paprika-800 dark:bg-paprika-900/20">
  <div class="flex items-center justify-between border-b border-paprika-200 bg-paprika-100 px-4 py-1.5 text-xs font-semibold uppercase tracking-wide text-paprika-700 dark:border-paprika-800 dark:bg-paprika-900/40 dark:text-paprika-300">
    <span>🍳 Empfehlung</span>
    <span title="Mit * markierte Links sind Affiliate-Links.">Anzeige</span>
  </div>
  <div class="flex flex-col gap-4 p-4 sm:flex-row sm:items-center sm:justify-between">
    <div>
      <p class="text-lg font-bold text-slate-900 dark:text-white">VPS 2000 G12</p>
      <p class="mt-1 text-sm text-slate-600 dark:text-slate-300">8 vCore · 16 GB RAM · 512 GB NVMe</p>
      <p class="mt-1 text-sm font-semibold text-paprika-700 dark:text-paprika-400">ab 19,24 €/Monat</p>
      <p class="mt-2 text-sm text-slate-600 dark:text-slate-400">Genug RAM und Speicher, um Monitoring neben deinen Apps mitlaufen zu lassen.</p>
    </div>
    <a href="https://www.netcup.com/de/server/vps/vps-2000-g12-12m?ref=44083" rel="sponsored noopener" target="_blank"
   class="inline-flex shrink-0 items-center justify-center rounded-lg bg-paprika-600 px-5 py-2.5 font-semibold text-white transition-colors hover:bg-paprika-700">
  Zu netcup →
</a>

  </div><div class="px-4 pb-4"><p class="not-prose my-3 flex flex-wrap items-center gap-x-2 gap-y-1 rounded-lg border border-herb-500/40 bg-herb-50 px-3 py-2 text-sm text-slate-700 dark:bg-herb-900/20 dark:text-slate-200">
  <span>💶 <strong>5 € Gutschein</strong> für netcup-Neukunden:</span>
  <code class="rounded bg-white px-2 py-0.5 font-mono text-sm font-semibold text-herb-800 dark:bg-slate-800 dark:text-herb-400">36nc17844976032</code>
  <span class="text-xs text-slate-500 dark:text-slate-400">(nur Neukunden, keine Domains)</span>
</p></div>
</div>

<h2 id="voraussetzungen">Voraussetzungen</h2>
<ul>
<li>Sicherheit im Umgang mit <strong>Docker Compose</strong> – Services, Volumes, Netzwerke. Falls das
noch neu ist: <a href="/tutorials/docker-compose-grundlagen/">Docker Compose verstehen</a>.</li>
<li>Ein laufender <strong>Traefik-Reverse-Proxy</strong> mit dem gemeinsamen <code>proxy</code>-Netzwerk und dem
Let&rsquo;s-Encrypt-Resolver <code>le</code> – wie in
<a href="/tutorials/reverse-proxy-traefik/">Reverse Proxy mit Traefik</a> aufgebaut.</li>
<li>Eine Subdomain <code>grafana.DEINE_DOMAIN</code>, deren DNS-Record (A/AAAA) auf deine Server-IP
zeigt – siehe <a href="/tutorials/domain-mit-server-verbinden/">Domain mit Server verbinden</a>.</li>
</ul>
<h2 id="schritt-für-schritt">Schritt für Schritt</h2>
<h3 id="schritt-1-die-architektur-verstehen">Schritt 1: Die Architektur verstehen</h3>
<p>Bevor wir Dateien anlegen, das Bild dahinter – sonst debuggst du blind. Prometheus
arbeitet nach dem <strong>Pull-Prinzip</strong>: Nicht die Dienste schicken ihre Werte irgendwohin,
sondern Prometheus fragt sie aktiv ab. Jeder „Exporter&quot; stellt seine Metriken unter
<code>/metrics</code> 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.</p>
<p>Warum überhaupt Pull statt Push? Weil Prometheus so jederzeit weiß, ob ein Ziel <em>lebt</em>:
Antwortet ein Exporter nicht, ist das selbst eine Information (<code>up = 0</code>). Du brauchst
keine Agenten, die aktiv „nach Hause telefonieren&quot;, und kannst jedes neue Ziel einfach in
die Konfiguration eintragen. Ein Exporter ist dabei nichts Magisches – er ist ein winziger
Webserver, der unter <code>/metrics</code> eine Textliste aktueller Messwerte ausliefert.</p>
<p>Daraus folgt die Sicherheitsregel dieses Setups: node-exporter, cAdvisor und Prometheus
haben <strong>keine Authentifizierung</strong>. Sie dürfen deshalb <strong>niemals</strong> offen im Internet
stehen. Prometheus und cAdvisor binden wir nur ins interne Docker-Netz <code>monitoring</code>;
node-exporter läuft im <strong>Host-Netz</strong> (warum, erklärt Schritt 4), bleibt aber dank
<a href="/tutorials/firewall-ufw-einrichten/">UFW</a> und
<a href="/tutorials/netcup-firewall-einrichten/">netcup-Firewall</a> von außen unerreichbar.
Ausschließlich Grafana hängt zusätzlich im <code>proxy</code>-Netz und bekommt eine Traefik-Route –
der einzige Dienst mit Login und öffentlicher Adresse.</p>
<h3 id="schritt-2-projektordner-und-prometheus-konfigurieren">Schritt 2: Projektordner und Prometheus konfigurieren</h3>
<p>Leg die Ordnerstruktur an:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir -p ~/monitoring/prometheus ~/monitoring/grafana/provisioning/datasources
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> ~/monitoring</span></span></code></pre></div>
</div>
<p>Erstelle die Prometheus-Konfiguration. Sie legt fest, <em>wen</em> Prometheus in welchem
Intervall abfragt:</p>
<div class="sk-code">
  <span class="sk-code-head">YAML</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c"># prometheus/prometheus.yml</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">global</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">scrape_interval</span><span class="p">:</span><span class="w"> </span><span class="l">15s</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">scrape_timeout</span><span class="p">:</span><span class="w"> </span><span class="l">10s</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">scrape_configs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">job_name</span><span class="p">:</span><span class="w"> </span><span class="l">prometheus</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">static_configs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">targets</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;localhost:9090&#34;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">job_name</span><span class="p">:</span><span class="w"> </span><span class="l">node</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">static_configs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">targets</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;host.docker.internal:9100&#34;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">job_name</span><span class="p">:</span><span class="w"> </span><span class="l">cadvisor</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">static_configs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">targets</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;cadvisor:8080&#34;</span><span class="p">]</span></span></span></code></pre></div>
</div>
<p>Wichtig: <code>cadvisor:8080</code> steht als <strong>Servicename</strong> da – Docker löst ihn im gemeinsamen
<code>monitoring</code>-Netz automatisch auf. Der node-exporter läuft dagegen im <strong>Host-Netz</strong>
(Schritt 4 erklärt, warum) und wird deshalb über <code>host.docker.internal:9100</code>
angesprochen – diesen Namen bekommt Prometheus gleich per <code>extra_hosts</code> auf die
IP des Docker-Hosts gemappt. <code>scrape_interval: 15s</code> ist ein guter Kompromiss – oft
genug für aussagekräftige Kurven, selten genug, um kaum Last zu erzeugen.</p>
<h3 id="schritt-3-grafana-datenquelle-automatisch-provisionieren">Schritt 3: Grafana-Datenquelle automatisch provisionieren</h3>
<p>Statt die Prometheus-Datenquelle später von Hand in Grafana anzuklicken, richten wir sie
per <strong>Provisioning</strong> ein – als Datei. Das ist reproduzierbar: Nach einem Neuaufbau ist
alles sofort wieder da, ohne Klickarbeit.</p>
<div class="sk-code">
  <span class="sk-code-head">YAML</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c"># grafana/provisioning/datasources/datasource.yml</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">datasources</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Prometheus</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">prometheus</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">access</span><span class="p">:</span><span class="w"> </span><span class="l">proxy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="l">http://prometheus:9090</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">isDefault</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">editable</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span></span></span></code></pre></div>
</div>
<p><code>access: proxy</code> bedeutet, dass <strong>Grafana selbst</strong> (serverseitig) Prometheus abfragt – der
Browser des Nutzers spricht nie direkt mit Prometheus. Genau deshalb kann Prometheus
intern bleiben.</p>
<h3 id="schritt-4-der-compose-stack">Schritt 4: Der Compose-Stack</h3>
<p>Jetzt das Herzstück. Erstelle die <code>compose.yaml</code>. Ersetze <code>grafana.DEINE_DOMAIN</code> und das
Grafana-Passwort:</p>
<div class="sk-code">
  <span class="sk-code-head">YAML</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">services</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">prometheus</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">prom/prometheus:v3.13.1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">container_name</span><span class="p">:</span><span class="w"> </span><span class="l">prometheus</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">command</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;--config.file=/etc/prometheus/prometheus.yml&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;--storage.tsdb.path=/prometheus&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;--storage.tsdb.retention.time=30d&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">prom_data:/prometheus</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">extra_hosts</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;host.docker.internal:host-gateway&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">networks</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">monitoring]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">restart</span><span class="p">:</span><span class="w"> </span><span class="l">unless-stopped</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">node-exporter</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">prom/node-exporter:v1.12.1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">container_name</span><span class="p">:</span><span class="w"> </span><span class="l">node-exporter</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">command</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;--path.rootfs=/host&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">network_mode</span><span class="p">:</span><span class="w"> </span><span class="l">host</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">pid</span><span class="p">:</span><span class="w"> </span><span class="l">host</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;/:/host:ro,rslave&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">restart</span><span class="p">:</span><span class="w"> </span><span class="l">unless-stopped</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">cadvisor</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">gcr.io/cadvisor/cadvisor:v0.55.1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">container_name</span><span class="p">:</span><span class="w"> </span><span class="l">cadvisor</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">privileged</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">devices</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">/dev/kmsg</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">/:/rootfs:ro</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">/var/run:/var/run:ro</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">/sys:/sys:ro</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">/var/lib/docker/:/var/lib/docker:ro</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">/dev/disk/:/dev/disk:ro</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">networks</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">monitoring]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">restart</span><span class="p">:</span><span class="w"> </span><span class="l">unless-stopped</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">grafana</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">grafana/grafana:13.1.0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">container_name</span><span class="p">:</span><span class="w"> </span><span class="l">grafana</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">depends_on</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">prometheus]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">GF_SECURITY_ADMIN_USER</span><span class="p">:</span><span class="w"> </span><span class="l">admin</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">GF_SECURITY_ADMIN_PASSWORD</span><span class="p">:</span><span class="w"> </span><span class="l">DEIN_GRAFANA_PASSWORT</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">GF_SERVER_ROOT_URL</span><span class="p">:</span><span class="w"> </span><span class="l">https://grafana.DEINE_DOMAIN</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">GF_USERS_ALLOW_SIGN_UP</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;false&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">grafana_data:/var/lib/grafana</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">./grafana/provisioning:/etc/grafana/provisioning:ro</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;traefik.enable=true&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;traefik.http.routers.grafana.rule=Host(`grafana.DEINE_DOMAIN`)&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;traefik.http.routers.grafana.entrypoints=websecure&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;traefik.http.routers.grafana.tls.certresolver=le&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="s2">&#34;traefik.http.services.grafana.loadbalancer.server.port=3000&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">networks</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">monitoring, proxy]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">restart</span><span class="p">:</span><span class="w"> </span><span class="l">unless-stopped</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">prom_data</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">grafana_data</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">networks</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">monitoring</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">proxy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">external</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span></span></span></code></pre></div>
</div>
<p>Was hier wichtig ist:</p>
<ul>
<li><strong><code>--storage.tsdb.retention.time=30d</code></strong> hält Metriken 30 Tage vor. Mehr = mehr
Aussagekraft, aber auch mehr Speicher. 30 Tage sind ein guter Start.</li>
<li><strong>node-exporter</strong> braucht das <strong>Host-Wurzelverzeichnis</strong> (<code>/:/host:ro,rslave</code>),
<code>pid: host</code> <strong>und <code>network_mode: host</code></strong>, um echte Host-Werte statt Container-Werte zu
lesen. <code>--path.rootfs=/host</code> sagt ihm, wo der Host-Blick liegt (nur lesend, <code>:ro</code>).
Das Host-Netz ist kein Nice-to-have: <code>/proc/net</code> ist an den Netzwerk-Namespace
gebunden – in einem normalen Bridge-Netz sähe node-exporter <strong>nur sein eigenes
Container-Interface</strong> (praktisch nur den Scrape-Traffic), nicht die echten
Host-Interfaces. Der Preis: node-exporter lauscht damit auf Port <code>9100</code> des Hosts –
deine <a href="/tutorials/firewall-ufw-einrichten/">Firewall</a> hält den Port von außen zu, und
Prometheus erreicht ihn über den <code>extra_hosts</code>-Eintrag (<code>host-gateway</code>).</li>
<li><strong>cAdvisor</strong> braucht <code>privileged: true</code>, das Gerät <code>/dev/kmsg</code> und Lese-Mounts auf
<code>/sys</code> und <code>/var/lib/docker</code>, um alle Container zu erkennen. Das ist für ein
Container-Monitoring unvermeidlich – deshalb bleibt cAdvisor strikt intern.</li>
<li><strong>Nur <code>grafana</code></strong> trägt Traefik-Labels und hängt im <code>proxy</code>-Netz. Der interne Port ist
<strong>3000</strong>. <code>GF_SERVER_ROOT_URL</code> muss die öffentliche HTTPS-Adresse sein, sonst brechen
Logins und Weiterleitungen hinter dem Proxy.</li>
<li><strong><code>GF_USERS_ALLOW_SIGN_UP: &quot;false&quot;</code></strong> verhindert, dass sich Fremde selbst ein Konto
anlegen. Setz ein <strong>starkes Admin-Passwort</strong> – Grafana steht öffentlich.</li>
</ul>
<h3 id="schritt-5-starten-und-die-prometheus-targets-prüfen">Schritt 5: Starten und die Prometheus-Targets prüfen</h3>
<p>Starte den kompletten Stack:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker compose up -d</span></span></code></pre></div>
</div>
<p>Beim ersten Mal lädt Docker vier Images – das dauert einen Moment. Prüfe, dass alle
Container laufen:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker compose ps</span></span></code></pre></div>
</div>
<p>Alle vier Dienste (node-exporter, cadvisor, prometheus, grafana) sollten
<code>running</code> sein. Jetzt der entscheidende Test: <strong>Sieht Prometheus seine Ziele?</strong> Da
Prometheus intern bleibt, fragen wir es aus einem kurzlebigen Container heraus, der im
selben Netz hängt (Compose nennt das Netz <code>monitoring_monitoring</code> – Projektordner plus
Netzname):</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker run --rm --network monitoring_monitoring curlimages/curl:8.21.0 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -s http://prometheus:9090/api/v1/query?query<span class="o">=</span>up</span></span></code></pre></div>
</div>
<p>In der JSON-Antwort steht für jedes Ziel ein <code>&quot;value&quot;</code> mit <code>&quot;1&quot;</code> – das heißt „erreichbar&quot;.
Im Test lieferten alle drei Jobs (<code>prometheus</code>, <code>node</code>, <code>cadvisor</code>) eine <code>1</code> zurück. Eine
<code>0</code> bedeutet, dass Prometheus das Ziel nicht scrapen kann – dann hilft der Abschnitt
„Wenn es nicht funktioniert&quot;.</p>
<h3 id="schritt-6-grafana-aufrufen-und-dashboards-importieren">Schritt 6: Grafana aufrufen und Dashboards importieren</h3>
<p>Ruf <code>https://grafana.DEINE_DOMAIN</code> auf. Traefik holt beim ersten Zugriff das
Zertifikat (ein paar Sekunden). Melde dich mit <code>admin</code> und deinem Passwort aus der
<code>compose.yaml</code> an. Dank Provisioning ist die Prometheus-Datenquelle bereits verbunden –
du findest sie unter <strong>Connections → Data sources</strong>:</p>
<p><figure class="my-6"><img src="/tutorials/monitoring-grafana-prometheus/grafana-datasource_hu_399dc2e7f48b1788.webp" srcset="/tutorials/monitoring-grafana-prometheus/grafana-datasource_hu_7e0a1a479133fa45.webp 480w, /tutorials/monitoring-grafana-prometheus/grafana-datasource_hu_399dc2e7f48b1788.webp 768w, /tutorials/monitoring-grafana-prometheus/grafana-datasource_hu_c28443e38bd35aa1.webp 1200w, /tutorials/monitoring-grafana-prometheus/grafana-datasource_hu_b685c9265d189cbd.webp 1920w" sizes="(min-width: 768px) 768px, 100vw"
    width="768" height="432"
    data-full="/tutorials/monitoring-grafana-prometheus/grafana-datasource_hu_2947ead1a73e98f1.webp"
    alt="Die provisionierte Prometheus-Datenquelle in Grafana mit der internen URL http://prometheus:9090" title="Die Datenquelle ist per Datei provisioniert – Grafana meldet sie als vorkonfiguriert"
    loading="lazy" decoding="async" class="rounded-lg"><figcaption class="mt-2 text-sm text-center text-slate-500 italic">Die Datenquelle ist per Datei provisioniert – Grafana meldet sie als vorkonfiguriert</figcaption></figure></p>
<p>Jetzt die Dashboards. Statt Panels selbst zu bauen, importieren wir zwei erprobte aus der
Grafana-Community. Geh auf <strong>Dashboards → New → Import</strong>, trag die ID ein und klick auf
<strong>Load</strong>:</p>
<p><figure class="my-6"><img src="/tutorials/monitoring-grafana-prometheus/grafana-import_hu_daeb0cdee97f29d5.webp" srcset="/tutorials/monitoring-grafana-prometheus/grafana-import_hu_ebf84a9b89d945c1.webp 480w, /tutorials/monitoring-grafana-prometheus/grafana-import_hu_daeb0cdee97f29d5.webp 768w, /tutorials/monitoring-grafana-prometheus/grafana-import_hu_b8420823a49d711c.webp 1200w, /tutorials/monitoring-grafana-prometheus/grafana-import_hu_26166f8d6195fe6c.webp 1920w" sizes="(min-width: 768px) 768px, 100vw"
    width="768" height="432"
    data-full="/tutorials/monitoring-grafana-prometheus/grafana-import_hu_c29ed6a2a42cab3a.webp"
    alt="Der Grafana-Import-Dialog lädt das Dashboard „Node Exporter Full&quot; von Grafana.com" title="Dashboard per ID importieren – hier „Node Exporter Full“ (ID 1860)"
    loading="lazy" decoding="async" class="rounded-lg"><figcaption class="mt-2 text-sm text-center text-slate-500 italic">Dashboard per ID importieren – hier „Node Exporter Full“ (ID 1860)</figcaption></figure></p>
<p>Importiere diese beiden:</p>
<ul>
<li><strong>1860</strong> – „Node Exporter Full&quot;: das umfassende Host-Dashboard.</li>
<li><strong>19792</strong> – „cAdvisor Dashboard&quot;: Metriken pro Container.</li>
</ul>
<p>Wähle beim Import jeweils die <strong>Prometheus</strong>-Datenquelle aus. Danach zeigt „Node Exporter
Full&quot; deinen Host in Echtzeit – CPU-Auslastung, belegter RAM, Festplatte, Netzwerk,
Uptime:</p>
<p><figure class="my-6"><img src="/tutorials/monitoring-grafana-prometheus/grafana-node-dashboard_hu_caaac511989edd0f.webp" srcset="/tutorials/monitoring-grafana-prometheus/grafana-node-dashboard_hu_96720c854316db6f.webp 480w, /tutorials/monitoring-grafana-prometheus/grafana-node-dashboard_hu_caaac511989edd0f.webp 768w, /tutorials/monitoring-grafana-prometheus/grafana-node-dashboard_hu_b90af51a41705c01.webp 1200w, /tutorials/monitoring-grafana-prometheus/grafana-node-dashboard_hu_3e6bb19e3a185f34.webp 1920w" sizes="(min-width: 768px) 768px, 100vw"
    width="768" height="432"
    data-full="/tutorials/monitoring-grafana-prometheus/grafana-node-dashboard_hu_d1052dba15ec2b7e.webp"
    alt="Das Grafana-Dashboard „Node Exporter Full&quot; zeigt Live-Werte des Servers: CPU 1 Prozent, RAM 14,7 Prozent belegt, 4 CPU-Kerne, 8 GB RAM, Uptime 2 Tage" title="Node Exporter Full mit echten Live-Daten des Test-Servers"
    loading="lazy" decoding="async" class="rounded-lg"><figcaption class="mt-2 text-sm text-center text-slate-500 italic">Node Exporter Full mit echten Live-Daten des Test-Servers</figcaption></figure></p>
<p>Das cAdvisor-Dashboard bricht dieselbe Last auf <strong>einzelne Container</strong> herunter – hier
siehst du sofort, welcher Dienst wie viel CPU und RAM zieht:</p>
<p><figure class="my-6"><img src="/tutorials/monitoring-grafana-prometheus/grafana-container-dashboard_hu_69ee0d4d1a780625.webp" srcset="/tutorials/monitoring-grafana-prometheus/grafana-container-dashboard_hu_9f644b6b9b88fc4f.webp 480w, /tutorials/monitoring-grafana-prometheus/grafana-container-dashboard_hu_69ee0d4d1a780625.webp 768w, /tutorials/monitoring-grafana-prometheus/grafana-container-dashboard_hu_db3e2021d7ef215.webp 1200w, /tutorials/monitoring-grafana-prometheus/grafana-container-dashboard_hu_41bcb30176acfef2.webp 1920w" sizes="(min-width: 768px) 768px, 100vw"
    width="768" height="432"
    data-full="/tutorials/monitoring-grafana-prometheus/grafana-container-dashboard_hu_412631eaba466c56.webp"
    alt="Das cAdvisor-Dashboard zeigt CPU-Verbrauch pro Container – jede Farbe ist ein Container wie grafana, prometheus oder traefik" title="cAdvisor: CPU- und Speicherverbrauch je Container, aufgeschlüsselt nach Name"
    loading="lazy" decoding="async" class="rounded-lg"><figcaption class="mt-2 text-sm text-center text-slate-500 italic">cAdvisor: CPU- und Speicherverbrauch je Container, aufgeschlüsselt nach Name</figcaption></figure></p>
<div class="not-prose my-6 rounded-lg border-l-4 p-4 border-herb-400 bg-herb-50 dark:border-herb-700 dark:bg-herb-900/20">
  <p class="mb-1 flex items-center gap-2 font-semibold text-slate-900 dark:text-white">
    <span aria-hidden="true">🧑‍🍳</span>Dashboards auch provisionieren
  </p>
  <div class="prose-kitchen text-sm">Wie die Datenquelle lassen sich auch Dashboards per Datei provisionieren (unter
<code>grafana/provisioning/dashboards/</code> eine <code>.yml</code> 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.</div>
</div>
<h3 id="schritt-7-die-wichtigsten-metriken-richtig-lesen">Schritt 7: Die wichtigsten Metriken richtig lesen</h3>
<p>Ein Dashboard voller Kurven nützt wenig, wenn du nicht weißt, worauf du achten musst.
Diese fünf Werte im „Node Exporter Full&quot;-Dashboard verraten dir fast alles über die
Gesundheit deines Servers:</p>
<ul>
<li><strong>CPU Busy.</strong> Kurze Spitzen sind normal. Kritisch wird es, wenn die Auslastung
<em>dauerhaft</em> hoch bleibt – dann ist der Server der Flaschenhals.</li>
<li><strong>Sys Load.</strong> 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).</li>
<li><strong>RAM Used.</strong> Wichtig: Linux nutzt freien Speicher als Datei-Cache – „belegt&quot; ist also
nicht gleich „knapp&quot;. Erst wenn <em>neben</em> dem Cache wenig frei bleibt und <strong>SWAP Used</strong>
steigt, wird der Server zäh. Genau diesen schleichenden Anstieg willst du früh sehen.</li>
<li><strong>Root FS Used.</strong> 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.</li>
<li><strong>Network Traffic.</strong> 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.</li>
</ul>
<p>Im cAdvisor-Dashboard ist die spannendste Frage: <strong>Welcher Container zieht die Last?</strong>
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&quot; ein
gezieltes „der Foto-Import in Container X frisst gerade die CPU&quot;.</p>
<h2 id="wenn-es-nicht-funktioniert">Wenn es nicht funktioniert</h2>
<p><strong>Symptom:</strong> Ein Target steht in Prometheus auf <code>up = 0</code> bzw. „DOWN&quot;.</p>
<p><strong>Ursache &amp; Lösung:</strong> Prometheus erreicht den Exporter nicht. Bei <code>cadvisor</code> prüfe, dass
er im selben <code>monitoring</code>-Netz läuft und Name und Port exakt stimmen (<code>cadvisor:8080</code>).
Beim <code>node</code>-Job prüfe den <code>extra_hosts</code>-Eintrag am Prometheus-Service und das Target
<code>host.docker.internal:9100</code> – ohne beides findet Prometheus den node-exporter im
Host-Netz nicht. Nach Änderungen an der <code>prometheus.yml</code> den Prometheus-Container neu
starten: <code>docker compose restart prometheus</code>.</p>
<p><strong>Symptom:</strong> Das cAdvisor-Dashboard zeigt teils „No data&quot; oder cAdvisor startet nicht.</p>
<p><strong>Ursache &amp; Lösung:</strong> Fehlen die Mounts oder <code>privileged: true</code>, sieht cAdvisor die
Container nicht. Kontrolliere den <code>cadvisor</code>-Block (Mounts, <code>devices: /dev/kmsg</code>). Einzelne
„No data&quot;-Panels sind normal – manche Metriken (etwa CPU-Drosselung) gibt es nur, wenn du
den Containern CPU-Limits gesetzt hast.</p>
<p><strong>Symptom:</strong> Grafana lädt hinter Traefik nicht richtig – Login schlägt fehl oder das
Layout ist kaputt.</p>
<p><strong>Ursache &amp; Lösung:</strong> Fast immer stimmt <code>GF_SERVER_ROOT_URL</code> nicht. Sie muss exakt die
öffentliche Adresse sein (<code>https://grafana.DEINE_DOMAIN</code>). Prüfe außerdem, dass die
Traefik-Label <code>loadbalancer.server.port=3000</code> gesetzt ist – Grafana lauscht intern auf 3000.</p>
<p><strong>Symptom:</strong> Grafana zeigt in den Panels „No data&quot;, obwohl die Targets <code>up</code> sind.</p>
<p><strong>Ursache &amp; Lösung:</strong> Meist der Zeitbereich. Ein frischer Stack hat noch keine Historie –
stell oben rechts „Last 15 minutes&quot; ein und warte ein paar Minuten, bis Daten aufgelaufen
sind. Prüfe zudem, dass beim Dashboard-Import die <strong>Prometheus</strong>-Datenquelle gewählt war.</p>
<p><strong>Symptom:</strong> Der Speicherplatz auf dem Server wächst stetig.</p>
<p><strong>Ursache &amp; Lösung:</strong> Das ist die Prometheus-TSDB. Reduziere <code>--storage.tsdb.retention.time</code>
(z. B. auf <code>15d</code>) oder überwache weniger Ziele. Das <code>prom_data</code>-Volume wächst mit Anzahl
der Metriken × Vorhaltezeit.</p>
<h2 id="wartung--backups">Wartung &amp; Backups</h2>
<ul>
<li><strong>Was du sichern musst.</strong> Wichtig ist das <strong><code>grafana_data</code>-Volume</strong>: Es enthält deine
Nutzer, Einstellungen und selbst gebauten Dashboards. Sichere es verschlüsselt und
off-site mit <a href="/tutorials/backups-mit-restic/">Restic</a>. Die Prometheus-Metriken
(<code>prom_data</code>) sind reine Messwerte – ein Verlust ist verschmerzbar, ein Backup optional.</li>
<li><strong>Updates.</strong> Die Image-Tags sind gepinnt (<code>prometheus:v3.13.1</code>, <code>grafana:13.1.0</code> …).
Zum Aktualisieren die Tags hochsetzen, dann <code>docker compose pull &amp;&amp; docker compose up -d</code>.
Lies vorher die Release-Notes – vor allem Grafana ändert zwischen Hauptversionen
gelegentlich Standards. cAdvisor beziehst du von <code>gcr.io</code>; dort ist <code>v0.55.1</code> derzeit die
aktuellste verfügbare Image-Version.</li>
<li><strong>Sicherheit.</strong> Grafana steht öffentlich – vergib ein starkes Admin-Passwort und lass
<code>GF_USERS_ALLOW_SIGN_UP</code> auf <code>false</code>. Prometheus und cAdvisor bleiben intern; exponiere
sie nicht „mal eben&quot; zum Debuggen, sie haben keine Authentifizierung.</li>
<li><strong>Der nächste Schritt: Alarme.</strong> Ein Dashboard hilft nur, wenn du hineinschaust.
Grafana kann bei Schwellwerten (RAM &gt; 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 <a href="/tutorials/uptime-kuma-monitoring/">Uptime Kuma</a>.</li>
<li><strong>Ehrlich zum Aufwand:</strong> 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.</li>
</ul>
]]></content:encoded></item></channel></rss>