🐳 Docker

LGTM im Homelab: Observability mit Grafana, Loki, Tempo, Mimir

LGTM im Homelab: Observability mit Grafana, Loki, Tempo, Mimir
⚠️ Hinweis: Alle Guides auf smoth.me dienen ausschließlich zu Informations- und Lernzwecken. Die Umsetzung erfolgt auf eigene Gefahr. Wir übernehmen keine Haftung für Schäden, Datenverluste oder Systemausfälle, die durch die Anwendung dieser Anleitungen entstehen können. → Vollständiger Haftungsausschluss

Moin zusammen! Als alter Hase im Homelab und mit jahrelanger Erfahrung in den Untiefen von Proxmox, Docker und Co. weiß ich, wie wichtig ein klarer Blick auf unsere Systeme ist. Gerade, wenn der Stack immer größer wird – sei es mit Home Assistant, n8n oder anderen Diensten – verliert man schnell den Überblick. Genau hier kommt Observability ins Spiel. Und heute sprechen wir über ein echtes Power-Paket: Den LGTM-Stack. Das steht für Loki, Grafana, Tempo und Mimir. Einmal richtig eingerichtet, wirst du dich fragen, wie du jemals ohne ausgekommen bist.

In meiner Erfahrung stolpern viele am Anfang über die schiere Menge an Tools und deren Konfiguration. Deswegen zeige ich dir heute, wie du LGTM in deinem Homelab mit Docker Compose aufsetzt – eine perfekte Basis, um später vielleicht auf Kubernetes umzusteigen, aber für den Anfang schön schlank und überschaubar.

Voraussetzungen für dein LGTM-Homelab

Bevor wir loslegen, lass uns sicherstellen, dass dein System bereit ist. Keine Sorge, die Anforderungen sind für ein typisches Homelab meist gut zu erfüllen.

Hardware-Anforderungen

  • CPU: Ein moderner Quad-Core-Prozessor ist ideal. LGTM kann ressourcenhungrig sein, besonders wenn du viele Logs und Metriken sammelst. Ein Intel i3/i5 (oder AMD-Äquivalent) der letzten Generation reicht für den Anfang.
  • RAM: Hier würde ich nicht sparen. Mindestens 8 GB RAM, besser 16 GB oder mehr. Grafana, Loki und Mimir können sich im Speicher breitmachen, besonders wenn sie Daten im Cache halten.
  • Speicher: Eine schnelle SSD ist Pflicht. Logs, Metriken und Traces werden kontinuierlich geschrieben. Eine NVMe-SSD ist natürlich top, aber eine gute SATA-SSD tut's auch. Plane mindestens 50-100 GB für die Daten ein, je nachdem, wie lange du deine Historie behalten möchtest. Denk daran, dass Logs und Metriken schnell wachsen können!

Software-Anforderungen

  • Linux-Distribution: Eine aktuelle Server-Distribution wie Ubuntu Server (LTS), Debian oder Rocky Linux.
  • Docker Engine: Die Docker Runtime muss installiert und lauffähig sein. Wer das noch nicht hat, findet hier die offizielle Anleitung.
  • Docker Compose: Wir werden Docker Compose für die Orchestrierung der einzelnen Dienste nutzen. Die Installation ist meist einfach:
    sudo apt update && sudo apt install docker-compose-plugin
    Das installiert das `docker compose` Plugin, das ist die moderne Art.
  • Grundlegende Linux-Kenntnisse: Du solltest dich auf der Kommandozeile wohlfühlen, Verzeichnisse anlegen und Dateien bearbeiten können (z.B. mit nano oder vim).

Netzwerk-Konfiguration

Stell sicher, dass die folgenden Ports auf deinem Hostsystem frei sind und du sie bei Bedarf in deiner Firewall öffnest:

  • 3000/TCP: Grafana Web-Interface
  • 3100/TCP: Loki Ingest-Port
  • 3200/TCP: Mimir Ingest-Port (für Prometheus Remote Write)
  • 4317/TCP: Tempo OTLP gRPC Port
  • 4318/TCP: Tempo OTLP HTTP Port

Für den Zugriff von außen, falls gewünscht, solltest du natürlich eine ordentliche Reverse-Proxy-Lösung wie Nginx Proxy Manager oder Caddy in Betracht ziehen. Für den Start arbeiten wir aber erstmal lokal.

Was ist LGTM und warum brauche ich es in meinem Homelab?

LGTM ist ein Akronym, das für Loki, Grafana, Tempo und Mimir steht. Es ist eine moderne, skalierbare Observability-Suite, die dir hilft, den Zustand deiner Anwendungen und Infrastruktur zu verstehen. Früher hat man oft Prometheus für Metriken und ELK für Logs verwendet. LGTM vereinfacht das und bietet eine engere Integration.

  • Grafana: Das Schweizer Taschenmesser unter den Visualisierungs-Tools. Hier siehst du alle deine Daten – Metriken, Logs und Traces – in übersichtlichen Dashboards. Es ist die zentrale Anlaufstelle für deine Überwachung.
  • Loki: Ein Log-Aggregator, der speziell für Logs entwickelt wurde. Anders als traditionelle Log-Systeme indiziert Loki nicht den gesamten Inhalt der Logs, sondern nur Metadaten (Labels). Das macht es extrem effizient und kostengünstig. Promtail ist der zugehörige Agent, der Logs sammelt und an Loki sendet.
  • Tempo: Ein leistungsstarkes Tracing-Backend. Tracing hilft dir, den Pfad einer Anfrage durch verschiedene Dienste zu verfolgen. Wenn du eine Microservices-Architektur hast (oder planst), ist Tempo Gold wert, um Latenzen und Fehlerquellen zu identifizieren.
  • Mimir: Ein hochskalierbares, mandantenfähiges Langzeitarchiv für Prometheus-Metriken. Mimir ist im Prinzip ein Prometheus-kompatibles Metrik-Backend, das die Skalierungs- und Langzeitspeicherprobleme von Standalone-Prometheus löst. Wir nutzen es hier als zentrale Metrik-Datenbank.

Warum brauchst du das? Ganz einfach: Wenn mal etwas schiefgeht – und das tut es immer – möchtest du nicht blind im Dunkeln tappen. Mit LGTM siehst du sofort, ob eine CPU am Limit ist, warum deine n8n-Workflows hängen oder welche Anfrage in deiner Home Assistant-Automatisierung die meiste Zeit braucht. Das spart dir unzählige Stunden beim Troubleshooting!

Vorbereitung: Unser Docker Compose Setup

Wir werden alle Komponenten in einem einzigen docker-compose.yaml file definieren. Das ist übersichtlich und einfach zu verwalten. Ich empfehle dir, dafür ein eigenes Verzeichnis anzulegen.

Schritt 1: Ordnerstruktur anlegen

Erstelle ein Hauptverzeichnis für deinen LGTM-Stack und darin Unterverzeichnisse für die Konfigurationsdateien und persistenten Daten der einzelnen Dienste. Das sorgt für Ordnung und erleichtert Backups.


mkdir -p ~/lgtm-stack
cd ~/lgtm-stack

mkdir -p config/grafana
mkdir -p config/loki
mkdir -p config/promtail
mkdir -p config/tempo
mkdir -p config/mimir
mkdir -p config/prometheus-agent

mkdir -p data/grafana
mkdir -p data/loki
mkdir -p data/tempo
mkdir -p data/mimir
mkdir -p data/prometheus-agent

Diese Struktur hilft uns, Konfigurationen und Daten getrennt zu halten. Die config/-Ordner werden die jeweiligen .yaml-Dateien enthalten, die wir später erstellen.

Schritt 2: Die docker-compose.yaml erstellen

Jetzt kommt das Herzstück: Die docker-compose.yaml. Wir definieren darin alle Dienste, ihre Konfigurationen, Volumes und Netzwerke. Öffne eine neue Datei: nano docker-compose.yaml und füge den folgenden Inhalt ein. Ich werde dir die wichtigsten Teile gleich detailliert erklären.


version: '3.8'

networks:
  lgtm-net:
    driver: bridge

volumes:
  grafana_data:
    driver: local
    driver_opts:
      type: none
      device: ${PWD}/data/grafana
      o: bind
  loki_data:
    driver: local
    driver_opts:
      type: none
      device: ${PWD}/data/loki
      o: bind
  tempo_data:
    driver: local
    driver_opts:
      type: none
      device: ${PWD}/data/tempo
      o: bind
  mimir_data:
    driver: local
    driver_opts:
      type: none
      device: ${PWD}/data/mimir
      o: bind
  prometheus_agent_data:
    driver: local
    driver_opts:
      type: none
      device: ${PWD}/data/prometheus-agent
      o: bind

services:
  grafana:
    image: grafana/grafana-oss:latest
    container_name: grafana
    ports:
      - "3000:3000"
    volumes:
      - grafana_data:/var/lib/grafana
      - ./config/grafana/provisioning:/etc/grafana/provisioning
    environment:
      - GF_SECURITY_ADMIN_USER=admin
      - GF_SECURITY_ADMIN_PASSWORD=your_secure_password # BITTE ÄNDERN!
    networks:
      - lgtm-net
    restart: unless-stopped

  loki:
    image: grafana/loki:latest
    container_name: loki
    ports:
      - "3100:3100"
    command: -config.file=/etc/loki/config.yaml
    volumes:
      - loki_data:/loki
      - ./config/loki/config.yaml:/etc/loki/config.yaml
    networks:
      - lgtm-net
    restart: unless-stopped

  promtail:
    image: grafana/promtail:latest
    container_name: promtail
    command: -config.file=/etc/promtail/config.yaml
    volumes:
      - ./config/promtail/config.yaml:/etc/promtail/config.yaml
      - /var/log:/var/log:ro # Host-Logs
      - /var/lib/docker/containers:/var/lib/docker/containers:ro # Docker Container Logs
      - /etc/hostname:/etc/hostname:ro # Für Hostname-Label
    networks:
      - lgtm-net
    depends_on:
      - loki
    restart: unless-stopped

  tempo:
    image: grafana/tempo:latest
    container_name: tempo
    ports:
      - "4317:4317" # OTLP gRPC
      - "4318:4318" # OTLP HTTP
      - "9411:9411" # Zipkin
    command: -config.file=/etc/tempo/config.yaml
    volumes:
      - tempo_data:/tmp/tempo
      - ./config/tempo/config.yaml:/etc/tempo/config.yaml
    networks:
      - lgtm-net
    restart: unless-stopped

  mimir:
    image: grafana/mimir:latest
    container_name: mimir
    ports:
      - "9009:9009" # Mimir HTTP/gRPC
      - "3200:3200" # Mimir Ingest (für Prometheus Remote Write)
    command: -config.file=/etc/mimir/config.yaml
    volumes:
      - mimir_data:/mimir
      - ./config/mimir/config.yaml:/etc/mimir/config.yaml
    networks:
      - lgtm-net
    restart: unless-stopped

  prometheus-agent:
    image: prom/prometheus:v2.48.0 # Eine stabile Version des Prometheus-Images
    container_name: prometheus-agent
    command: --config.file=/etc/prometheus/prometheus.yml --web.listen-address=:8080 --enable-feature=agent --storage.agent.path=/data
    volumes:
      - prometheus_agent_data:/data
      - ./config/prometheus-agent/prometheus.yml:/etc/prometheus/prometheus.yml
    ports:
      - "8080:8080" # Optional, falls du das Agent UI sehen willst
    networks:
      - lgtm-net
    depends_on:
      - mimir
    restart: unless-stopped

  node-exporter:
    image: prom/node-exporter:latest
    container_name: node-exporter
    ports:
      - "9100:9100"
    command:
      - '--path.procfs=/host/proc'
      - '--path.sysfs=/host/sys'
      - '--path.rootfs=/rootfs'
      - '--collector.filesystem.mount-points-exclude=^/(dev|proc|sys|var/lib/docker/.+|var/run/docker/.+)($|/)'
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    networks:
      - lgtm-net
    restart: unless-stopped

Wichtige Erklärungen zur docker-compose.yaml:

  • networks und volumes: Wir definieren ein internes Netzwerk lgtm-net, damit die Dienste miteinander kommunizieren können, ohne Ports nach außen zu exponieren. Die Volumes sorgen dafür, dass deine Daten (Grafana-Dashboards, Logs, Metriken, Traces) persistent gespeichert werden und nicht verloren gehen, wenn Container neu erstellt werden. Die driver_opts sorgen dafür, dass die Volumes direkt auf die von uns angelegten Ordner auf dem Host gemappt werden.
  • grafana: Standard Grafana-Image. Ganz wichtig: Ändere das Standardpasswort your_secure_password! Das provisioning-Volume werden wir später nutzen, um Grafana automatisch mit Datenquellen zu bestücken.
  • loki: Startet Loki und bindet unsere Konfigurationsdatei ein.
  • promtail: Der Log-Collector für Loki. Er hat spezielle Volume-Mounts, um auf die Log-Dateien des Host-Systems (/var/log) und die Docker-Container-Logs (/var/lib/docker/containers) zugreifen zu können. Das /etc/hostname-Mount ist nützlich, um den Hostnamen als Label hinzuzufügen.
  • tempo: Das Tracing-Backend. Auch hier wird eine Konfigurationsdatei eingebunden. Es exponiert Ports für OpenTelemetry (OTLP) und Zipkin.
  • mimir: Unser Metrik-Backend. Es exponiert Port 3200 für das Remote Writing von Prometheus-Agenten und 9009 für seine interne API.
  • prometheus-agent: Eine spezielle Prometheus-Instanz, die im Agent-Modus läuft. Sie sammelt Metriken (z.B. vom node-exporter) und schreibt sie dann via remote_write an Mimir. Das ist eine schlanke Art, Metriken zu sammeln, ohne die volle Prometheus-Funktionalität zu benötigen.
  • node-exporter: Sammelt Systemmetriken deines Host-Systems (CPU, RAM, Disk I/O, Netzwerk). Der Prometheus-Agent wird diesen Dienst später scrapen. Die Volume-Mounts sind notwendig, damit der Node Exporter korrekten Zugriff auf die Systeminformationen hat.

Schritt 3: Konfigurationsdateien erstellen

Jetzt erstellen wir die Konfigurationsdateien für Loki, Promtail, Tempo, Mimir und den Prometheus-Agenten. Diese müssen in die zuvor angelegten config/-Ordner.

config/loki/config.yaml

Die Loki-Konfiguration ist für den Anfang recht einfach. Wir definieren das Speichersystem (hier "boltdb-shipper" für lokale Speicherung und "filesystem" für Index und Chunks).


auth_enabled: false

server:
  http_listen_port: 3100
  grpc_listen_port: 9095

common:
  path_prefix: /loki
  replication_factor: 1
  storage_config:
    boltdb_shipper:
      active_index_directory: /loki/boltdb-shipper-active
      cache_location: /loki/boltdb-shipper-cache
      cache_ttl: 24h         # optional
      shared_store: filesystem
    filesystem:
      directory: /loki/chunks
  ring:
    instance_addr: 127.0.0.1
    kvstore:
      store: inmemory

schema_config:
  configs:
    - from: 2020-10-24
      store: boltdb-shipper
      object_store: filesystem
      schema: v11
      period: 24h

config/promtail/config.yaml

Promtail holt die Logs und schickt sie an Loki. Hier konfigurieren wir zwei Scraper: einen für die Docker-Container-Logs und einen für die Host-System-Logs (z.B. syslog).


server:
  http_listen_port: 9080
  grpc_listen_port: 0

positions:
  filename: /tmp/positions.yaml

clients:
  - url: http://loki:3100/loki/api/v1/push

scrape_configs:
  - job_name: system
    static_configs:
      - targets:
          - localhost
        labels:
          job: varlogs
          __path__: /var/log/*log
          host: __HOSTNAME__ # Das wird vom /etc/hostname Mount ersetzt
    pipeline_stages:
      - cri: {} # Für Docker Container Logs, falls du Promtail auch auf Docker-Logs anwenden willst

  - job_name: docker
    docker_sd_configs:
      - host: unix:///var/run/docker.sock
        refresh_interval: 5s
    relabel_configs:
      - source_labels: ['__meta_docker_container_name']
        regex: '/(.*)'
        target_label: 'container'
      - source_labels: ['__meta_docker_container_id']
        target_label: 'instance'
      - source_labels: ['__meta_docker_image']
        target_label: 'image'
      - source_labels: ['__meta_docker_container_label_com_docker_compose_service']
        target_label: 'compose_service'
      - source_labels: ['__meta_docker_container_label_com_docker_compose_project']
        target_label: 'compose_project'
      - target_label: job
        replacement: docker-logs

Wichtig: Für den docker_sd_configs-Teil in Promtail müsstest du normalerweise /var/run/docker.sock als Volume in den Promtail-Container mounten. In unserem docker-compose.yaml habe ich das weggelassen, um die Komplexität zu reduzieren. Wenn du Docker-Logs haben möchtest, müsstest du diese Zeile zum Promtail-Service hinzufügen: - /var/run/docker.sock:/var/run/docker.sock:ro.

config/tempo/config.yaml

Tempo konfigurieren wir für den Anfang mit einem einfachen Dateisystem-Speicher. Für größere Setups würde man hier S3-kompatiblen Speicher oder GCS/Azure Blob Storage nutzen.


server:
  http_listen_port: 3200
  grpc_listen_port: 9095

distributor:
  receivers:
    otlp:
      protocols:
        grpc:
        http:
    zipkin:
    jaeger:
      protocols:
        thrift_http:
        grpc:
        thrift_compact:
        thrift_binary:

ingester:
  trace_idle_period: 10s
  max_block_duration: 5m

storage:
  trace:
    backend: local
    local:
      path: /tmp/tempo/blocks
    wal:
      path: /tmp/tempo/wal

config/mimir/config.yaml

Mimir ist ein komplexes System, aber für den Homelab-Einsatz können wir es stark vereinfachen und als "monolithischen" Dienst laufen lassen. Die Konfiguration ist minimal, um es zum Laufen zu bringen.


target: all

server:
  http_listen_port: 9009
  grpc_listen_port: 9095

blocks_storage:
  backend: filesystem
  filesystem:
    dir: /mimir/data

ruler:
  storage:
    type: filesystem
    filesystem:
      dir: /mimir/data/rules

alertmanager_storage:
  type: filesystem
  filesystem:
    dir: /mimir/data/alertmanager

compactor:
  data_dir: /mimir/data/compactor

store_gateway:
  sharding_enabled: false

# Minimal config for a single node setup
ingester:
  ring:
    instance_addr: 127.0.0.1
    kvstore:
      store: inmemory

distributor:
  ring:
    instance_addr: 127.0.0.1
    kvstore:
      store: inmemory

config/prometheus-agent/prometheus.yml

Dies ist die Konfiguration für unseren Prometheus-Agenten. Er scrapt den node-exporter und sendet die Metriken an Mimir.


global:
  scrape_interval: 15s # Scrape targets every 15 seconds.
  evaluation_interval: 15s # Evaluate rules every 15 seconds.

scrape_configs:
  - job_name: 'node_exporter'
    static_configs:
      - targets: ['node-exporter:9100'] # Der Dienstname aus docker-compose.yaml

remote_write:
  - url: http://mimir:3200/api/v1/push # Mimir Ingest-Endpoint
    queue_config:
      max_samples_per_send: 500
      batch_send_deadline: 5s
      capacity: 10000

Schritt 4: Erste Konfiguration und Start

Nachdem alle Dateien an Ort und Stelle sind, können wir den Stack starten. Geh in dein ~/lgtm-stack Verzeichnis und führe aus:


docker compose up -d

Der Befehl -d sorgt dafür, dass die Container im Hintergrund laufen. Du kannst den Status der Container mit docker compose ps überprüfen.

Solltest du Probleme haben, sieh dir die Logs an: docker compose logs -f (-f für "follow").

Schritt 5: Grafana einrichten

Öffne deinen Browser und navigiere zu http://localhost:3000 (oder die IP deines Homelab-Servers). Melde dich mit admin und deinem zuvor gesetzten Passwort an. Wenn du es nicht geändert hast, ist es your_secure_passwordbitte ändere es sofort nach dem ersten Login!

Datenquellen in Grafana hinzufügen

Geh im Grafana-Menü zu Connections -> Data sources und klicke auf Add data source.

  1. Loki:
    • Wähle "Loki" aus.
    • URL: http://loki:3100
    • Klicke auf "Save & test". Es sollte "Data source is working" erscheinen.
  2. Tempo:
    • Wähle "Tempo" aus.
    • URL: http://tempo:3200 (Tempo's HTTP-Port, nicht OTLP!)
    • Optional: Verknüpfe es mit Loki für Log-Kontext (unter "Service Graph" und "Trace to logs" Einstellungen).
    • Klicke auf "Save & test".
  3. Mimir:
    • Wähle "Prometheus" aus (Mimir ist Prometheus-kompatibel).
    • URL: http://mimir:9009 (Mimir's HTTP-Port)
    • Klicke auf "Save & test".

Weitere Guides aus "Docker"

Helm im Homelab: Kubernetes-Anwendungen effizient verwalten
Lerne, wie du Kubernetes-Applikationen mit Helm in deinem Homelab oder als Admin effizient verwaltes…
K3s im Homelab: Dein erster Kubernetes-Cluster mit Proxmox
Erfahre, wie du einen schlanken K3s Kubernetes-Cluster in deinem Homelab auf Proxmox-VMs aufsetzt. E…
Super Productivity 18.0 im Homelab mit Docker
Ich zeige dir, wie du Super Productivity 18.0 in deinem Homelab mit Docker betreibst und seine neuen…
Souveräne Passwortverwaltung: Dein eigener Vaultwarden Server
Raus aus der Cloud, rein ins Homelab! Dieser Guide zeigt dir, wie du deinen eigenen Bitwarden-kompat…