LGTM im Homelab: Observability mit Grafana, Loki, Tempo, Mimir
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:
Das installiert das `docker compose` Plugin, das ist die moderne Art.sudo apt update && sudo apt install docker-compose-plugin - 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:
networksundvolumes: Wir definieren ein internes Netzwerklgtm-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. Diedriver_optssorgen 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 Standardpasswortyour_secure_password! Dasprovisioning-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. vomnode-exporter) und schreibt sie dann viaremote_writean 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_password – bitte ä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.
- Loki:
- Wähle "Loki" aus.
- URL:
http://loki:3100 - Klicke auf "Save & test". Es sollte "Data source is working" erscheinen.
- 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".
- Mimir:
- Wähle "Prometheus" aus (Mimir ist Prometheus-kompatibel).
- URL:
http://mimir:9009(Mimir's HTTP-Port) - Klicke auf "Save & test".