Logs zentralisieren mit Grafana Loki
So sammelst du alle Logs deiner Linux-Server und Docker-Container an einem Ort: Diese Anleitung zeigt den self-hosted Loki-Stack mit Docker Compose, Grafana Alloy als Collector und Grafana als Oberfläche – inklusive LogQL-Grundlagen, Loki-Datenquelle, Explore-Abfragen und aktivierter Retention.

Wer mehrere Linux-Server und Docker-Container betreibt, kennt das Problem: Bei einem Fehler musst du dich per SSH auf jede Maschine einloggen und einzeln durch journalctl und Logdateien wühlen. Logs zentralisieren mit Grafana Loki löst das, indem alle Logs an einem Ort landen und sich dort durchsuchen lassen – self-hosted, in Docker, ohne teure SaaS-Lösung. Diese Anleitung baut einen kompakten Loki-Stack aus Loki, Grafana Alloy und Grafana auf, bindet ihn an dein Grafana an und zeigt die wichtigsten LogQL-Abfragen.
Voraussetzungen
Bevor du startest, sollte folgendes vorhanden sein:
- Ein Linux-Server mit installiertem Docker und Docker Compose (Plugin
docker composev2). - Ein Benutzer mit Rechten, den Docker-Daemon zu nutzen, und Lesezugriff auf den Docker-Socket
/var/run/docker.sock(für Container-Logs). - Etwas Plattenplatz für die Log-Chunks unter dem Loki-Volume – wie viel, hängt von Log-Volumen und Retention ab.
- Die Ports 3000 (Grafana), 3100 (Loki) und optional 12345 (Alloy-UI) frei. Diese Ports gehören nicht ungeschützt ins Internet.
- Grundkenntnisse in YAML und der Kommandozeile. Für reine Host-Logs ohne Loki bleibt
journalctldie einfachste Wahl (Link am Ende).
Diese Anleitung baut bewusst einen monolithischen Single-Node-Stack (Loki mit -target=all und Filesystem-Storage). Der offizielle Getting-Started-Stack ist die skalierbare Variante mit read/write/backend, MinIO und nginx-Gateway – für einen einzelnen Host meist überdimensioniert.
Schritt 1: Architektur verstehen
Der Stack besteht aus drei klar getrennten Rollen. Wer diese Trennung versteht, debuggt später deutlich schneller:
| Komponente | Rolle | Aufgabe | Port |
|---|---|---|---|
| Grafana Loki | Log-Datenbank / Aggregation | Speichert Logs als komprimierte Chunks, indexiert nur die Labels (nicht den Volltext), stellt die Push- und Query-API bereit | 3100 |
| Grafana Alloy | Collector / Agent | Sammelt Logs (Docker, journald, Logdateien) und pusht sie an die Loki-API | 12345 (UI) |
| Grafana | Oberfläche | Fragt Loki via LogQL ab, zeigt Logs in Explore und in Dashboards | 3000 |
Der Datenfluss ist einfach: Alloy liest Logs ein und schickt sie an den Push-Endpunkt von Loki. Aus Sicht der Container im selben Compose-Netz ist das http://loki:3100/loki/api/v1/push, von außen http://localhost:3100/.... Loki legt die Daten ab, Grafana liest sie wieder aus.
Der entscheidende Punkt: Loki indexiert nur Labels (z. B. job, container, level). Den eigentlichen Log-Text durchsuchst du erst zur Abfragezeit mit Zeilenfiltern. Deshalb gilt die wichtigste Regel von Loki: halte die Label-Kardinalität niedrig – niemals Request-IDs, IP-Adressen oder Timestamps als Labels verwenden.
Schritt 2: Docker-Compose-Stack anlegen
Lege ein Projektverzeichnis an, z. B. /opt/loki-stack, und darin eine compose.yaml. Das folgende Gerüst ist ein kompakter Single-Node-Stack mit gepinnten Versionen, persistentem Loki-Volume und den drei Diensten:
services:
loki:
image: grafana/loki:3.7.0
container_name: loki
command: -config.file=/etc/loki/loki-config.yaml
ports:
- "127.0.0.1:3100:3100" # nur lokal binden, nicht offen ins Netz
volumes:
- ./loki-config.yaml:/etc/loki/loki-config.yaml:ro
- loki-data:/loki
restart: unless-stopped
alloy:
image: grafana/alloy:v1.7.5
container_name: alloy
command:
- run
- --server.http.listen-addr=0.0.0.0:12345
- /etc/alloy/config.alloy
ports:
- "127.0.0.1:12345:12345" # Alloy-Web-UI, nur lokal
volumes:
- ./config.alloy:/etc/alloy/config.alloy:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
depends_on:
- loki
restart: unless-stopped
grafana:
image: grafana/grafana:11.5.2
container_name: grafana
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=bitte-aendern
volumes:
- ./grafana-datasources.yaml:/etc/grafana/provisioning/datasources/ds.yaml:ro
- grafana-data:/var/lib/grafana
depends_on:
- loki
restart: unless-stopped
volumes:
loki-data: {}
grafana-data: {}
Wichtig sind hier drei Details: Die Image-Tags sind auf feste Versionen gepinnt statt :latest (sonst Reproduzierbarkeits- und Upgrade-Probleme), Loki und Alloy binden ihre Ports nur an 127.0.0.1, und Alloy bekommt den Docker-Socket read-only hineingemountet – ohne diesen Mount sieht Alloy keine Container-Logs.
Schritt 3: loki-config.yaml schreiben
Loki braucht eine Konfigurationsdatei. Diese Basis-Config läuft monolithisch (target: all) mit Filesystem-Storage und aktiviert bereits die Retention (Details dazu in Schritt 8):
auth_enabled: false # kein Multi-Tenant; Loki bringt KEINE Auth mit
server:
http_listen_port: 3100
log_level: info
common:
instance_addr: 127.0.0.1
path_prefix: /loki
storage:
filesystem:
chunks_directory: /loki/chunks
rules_directory: /loki/rules
replication_factor: 1
ring:
kvstore:
store: inmemory
schema_config:
configs:
- from: 2024-01-01
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index_
period: 24h # Index-Periode MUSS 24h sein (Retention-Voraussetzung)
compactor:
working_directory: /loki/retention
compaction_interval: 10m
retention_enabled: true
retention_delete_delay: 2h
retention_delete_worker_count: 150
delete_request_store: filesystem # Pflicht, sobald Retention aktiv ist
limits_config:
retention_period: 744h # 31 Tage; 0s (Default) = unbegrenzt
max_query_lookback: 744h # sollte <= retention_period sein
Die wichtigsten Stellschrauben: auth_enabled: false bedeutet, dass Loki keinerlei Zugriffsschutz mitbringt (dazu Schritt 7). Die index.period muss exakt 24h betragen, sonst greift die Retention nicht. Und retention_period legt fest, wie lange Logs aufbewahrt werden – ohne diesen Wert (Default 0s) wachsen deine Logs unbegrenzt.
Schritt 4: Alloy als Collector konfigurieren
Alloy nutzt eine eigene Konfigurationssprache (River-Syntax, Datei config.alloy). Die folgende Pipeline entdeckt alle laufenden Docker-Container, nimmt den Container-Namen als Label und schickt deren Logs an Loki:
discovery.docker "linux" {
host = "unix:///var/run/docker.sock"
}
discovery.relabel "docker" {
targets = []
rule {
source_labels = ["__meta_docker_container_name"]
regex = "/(.*)"
target_label = "container"
}
}
loki.source.docker "default" {
host = "unix:///var/run/docker.sock"
targets = discovery.docker.linux.targets
labels = {"job" = "docker"}
relabel_rules = discovery.relabel.docker.rules
forward_to = [loki.write.local.receiver]
}
loki.write "local" {
endpoint {
url = "http://loki:3100/loki/api/v1/push"
}
}
Damit hast du nach dem Start jeden Container-Log unter dem Label {job="docker"} und zusätzlich pro Container unter {container="..."}. Die Alloy-Web-UI unter http://localhost:12345 zeigt dir, ob die Pipeline-Komponenten gesund sind und Daten fließen.
Für Host-Logs (journald/systemd, klassische Logdateien) ergänzt du in Alloy eine loki.source.journal-Quelle bzw. local.file_match auf /var/log/syslog oder /var/log/messages. Dafür muss der jeweilige Pfad (z. B. /var/log/journal oder /var/log) als read-only Volume in den Alloy-Container gemountet sein – fehlt der Mount, kommen keine Host-Logs an.
Schritt 5: Loki als Datenquelle in Grafana
Am saubersten richtest du die Datenquelle per Provisioning ein. Lege im Projektverzeichnis eine grafana-datasources.yaml an (sie wird im Compose-File nach /etc/grafana/provisioning/datasources/ gemountet):
apiVersion: 1
datasources:
- name: Loki
type: loki
access: proxy
url: http://loki:3100
isDefault: false
Beim nächsten Start steht Loki dann automatisch als Datenquelle bereit. Jetzt den Stack starten und Loki auf Bereitschaft prüfen:
cd /opt/loki-stack
docker compose up -d
# Health-Check: muss "ready" liefern
curl http://localhost:3100/ready
# Laufen alle drei Container?
docker compose ps
Alternativ richtest du die Datenquelle manuell ein. Öffne Grafana unter http://localhost:3000 (Erst-Login admin / dein gesetztes Passwort) und folge dem Klickpfad:
- Connections → Data sources → Add data source
- Den Typ Loki auswählen
- Bei URL
http://loki:3100eintragen - Unten auf Save & test klicken – es muss eine grüne Erfolgsmeldung erscheinen
Schritt 6: LogQL-Grundlagen und Explore
LogQL ist an PromQL angelehnt. Pflicht ist immer ein Label-Selektor in geschweiften Klammern, danach folgt optional eine Pipeline. Du testest Abfragen am besten in Explore (linkes Menü, Datenquelle Loki auswählen). Die Selektor-Operatoren:
=exakt,!=ungleich,=~Regex-Match,!~Regex kein Match – z. B.{job="docker", container=~"web.+"}- Zeilenfilter (schnellste Methode nach dem Selektor):
|=enthält,!=enthält nicht,|~Regex enthält,!~Regex fehlt - Parser:
| jsonund| logfmtextrahieren Felder als Labels
Ein paar copy-paste-fertige Beispiele:
{job="docker"} # alle Container-Logs
{container="nginx"} |= "error" # Zeilen, die 'error' enthalten
{job="docker"} |= "error" != "timeout" # verkettete Zeilenfilter
{job="docker"} | json | level="error" # JSON parsen, dann nach Feld filtern
Für Graphen brauchst du Metrik-Queries. Sie wandeln Logzeilen in Zahlenreihen um – etwa Einträge pro Sekunde oder Fehler je Container:
rate({job="docker"}[5m]) # Eintraege pro Sekunde
count_over_time({job="docker"} |= "error" [5m]) # Fehler-Anzahl im 5m-Fenster
sum by (container) (
count_over_time({job="docker"} |= "error" [5m])
) # Fehler je Container
Merke dir die Unterscheidung: Ein nackter Selektor mit Zeilenfilter liefert Logzeilen (Logs-Panel / Explore), während rate(), count_over_time() oder sum by() eine Metrik für ein Graph-Panel ergeben. Verwechselst du beides, bleibt die Darstellung leer oder unerwartet.
Schritt 7: Log-Dashboard bauen und Loki absichern
Aus Explore heraus baust du schnell ein einfaches Dashboard. Der Klickpfad für ein Logs-Panel:
- Abfrage in Explore testen, z. B.
{job="docker"} |= "error" - Oben rechts auf Add to dashboard → New dashboard
- Den Panel-Typ auf Logs stellen, Panel benennen und speichern
- Ein zweites Panel vom Typ Time series mit einer Metrik-Query wie
sum by (container) (count_over_time({job="docker"} |= "error" [5m]))hinzufügen
Damit erweiterst du dein bestehendes Grafana-Metriken-Dashboard um eine Log-Sicht: Metriken zeigen dass etwas klemmt, die Logs zeigen warum. Wie du Panels und Datenquellen grundsätzlich aufbaust, ist im verlinkten Grafana-Dashboard-Guide weiter unten beschrieben.
Zur Sicherheit ein klares Wort: Loki bringt keine Authentifizierung mit. Der offene Push-Endpunkt auf Port 3100 darf niemals ungeschützt ins Netz oder Internet. Setze davor:
- Einen authentifizierenden Reverse-Proxy (nginx oder Traefik) mit Basic-Auth oder OAuth, falls Loki über das Netz erreichbar sein muss.
- Eine Firewall-Regel, die 3100 (und 12345) nur für vertrauenswürdige Hosts öffnet – im Beispiel oben binden wir die Ports bereits nur an
127.0.0.1. - Ein starkes Grafana-Admin-Passwort statt des Default
admin.
Schritt 8: Retention und Limits richtig setzen
Ohne Retention wachsen deine Logs unbegrenzt – der häufigste Stolperstein im Betrieb. Die Aktivierung hängt an drei Bedingungen, die in der loki-config.yaml aus Schritt 3 schon erfüllt sind:
| Einstellung | Wert / Bedeutung |
|---|---|
compactor.retention_enabled | true – schaltet das Löschen alter Daten überhaupt erst ein |
compactor.delete_request_store | filesystem – Pflicht, sobald Retention aktiv ist |
limits_config.retention_period | z. B. 744h (31 Tage); Minimum 24h; 0s = unbegrenzt |
schema_config ... index.period | muss exakt 24h sein (single-store TSDB) |
retention_delete_delay | Default 2h – so lange bleiben gelöschte Chunks noch liegen |
Brauchst du unterschiedliche Aufbewahrung je Log-Quelle, definierst du eine Stream-Retention mit Selektor, Priorität und Periode:
limits_config:
retention_period: 744h # globaler Default: 31 Tage
retention_stream:
- selector: '{job="docker", container="nginx"}'
priority: 1
period: 168h # Access-Logs nur 7 Tage
Zwei Dinge sorgen oft für Verwirrung: Gelöschte Chunks geben den Plattenplatz erst nach retention_delete_delay (Default 2h) frei – der Speicher sinkt also nicht sofort. Und max_query_lookback sollte höchstens so groß wie retention_period sein, sonst fragst du Zeiträume ab, in denen ohnehin keine Daten mehr liegen.
Troubleshooting / Typische Fehler
- Keine Container-Logs in Loki: Fehlt der Mount
/var/run/docker.sockim Alloy-Container? Prüfe die Alloy-UI auf 12345 unddocker compose logs alloy. - Push schlägt fehl / Connection refused: Die Alloy-URL muss
http://loki:3100/loki/api/v1/pushlauten (Service-Name im Compose-Netz), nichtlocalhost. - Logs wachsen unbegrenzt: Retention ist standardmäßig aus.
retention_enabled: true,delete_request_storeundretention_periodsetzen. - Retention greift nicht: Die
index.periodmuss exakt 24h sein und die Retention mindestens 24h betragen. - Loki langsam oder überlastet: Meist zu hohe Label-Kardinalität. Niemals Request-IDs, IPs oder Timestamps als Labels – solche Details über Zeilenfilter und
| json/| logfmtabfragen. - Explore zeigt nichts an: Log- und Metrik-Query verwechselt? Nackter Selektor = Logs,
rate()/count_over_time()= Metrik/Graph. - Upgrade kaputt:
:latestin den Images vermeiden und auf feste Versionen pinnen (z. B.grafana/loki:3.7.0).
Häufige Fragen
Soll ich Promtail oder Grafana Alloy nehmen?
Für jede Neuinstallation Grafana Alloy. Promtail ist seit dem 2. März 2026 End-of-Life und bekommt keine Updates oder Security-Fixes mehr; seine Funktionen sind mit Loki 3.4 in Alloy aufgegangen. Bestehende Promtail-Configs lassen sich mit einem Migrationstool in Alloy-Config umwandeln.
Indexiert Loki den gesamten Log-Text?
Nein. Loki indexiert ausschließlich die Labels und speichert den eigentlichen Text als komprimierte Chunks. Den Volltext durchsuchst du erst zur Abfragezeit über Zeilenfilter (|=, |~) und Parser (| json). Das ist der Grund, warum Loki vergleichsweise sparsam mit Speicher und Ressourcen umgeht.
Wie sammle ich Host-Logs wie journald oder /var/log/syslog ein?
In Alloy ergänzt du eine loki.source.journal-Quelle bzw. einen Datei-Match auf /var/log/syslog und mountest den jeweiligen Pfad read-only in den Alloy-Container. Für schnelles Lesen direkt auf dem Host ohne Loki bleibt journalctl die einfachste Wahl – siehe den verlinkten Leitfaden unten.
Brauche ich den großen Getting-Started-Stack mit MinIO und nginx?
Für einen einzelnen Server nicht. Der offizielle Getting-Started-Stack ist die skalierbare Variante (read/write/backend + MinIO + nginx-Gateway) und für kleine Setups überdimensioniert. Ein monolithisches Loki (target: all) mit Filesystem-Storage – wie in dieser Anleitung – reicht völlig.
Ist mein Loki ohne weitere Maßnahmen sicher erreichbar?
Nein. Loki bringt keine Authentifizierung mit. Stelle Port 3100 niemals offen ins Netz, sondern binde ihn nur lokal, setze einen authentifizierenden Reverse-Proxy davor und schütze ihn per Firewall.
Warum sinkt mein Plattenplatz nach dem Setzen der Retention nicht sofort?
Weil gelöschte Chunks erst nach retention_delete_delay (Default 2 Stunden) tatsächlich entfernt werden. Zudem läuft der Compactor nur alle compaction_interval (Default 10 Minuten). Etwas Geduld – der Platz wird mit Verzögerung frei.
Fazit
Mit Loki, Grafana Alloy und Grafana hast du eine schlanke, self-hosted Log-Zentrale, die ohne SaaS-Kosten auskommt. Der Kern ist die saubere Rollentrennung: Alloy sammelt und pusht, Loki speichert und indexiert nur Labels, Grafana fragt via LogQL ab. Halte dich an die drei wichtigsten Regeln – Label-Kardinalität niedrig halten, Loki niemals offen ins Netz stellen und die Retention aktiv setzen – dann skaliert der Stack zuverlässig vom einzelnen Server bis zum kleinen KMU-Cluster. Zusammen mit deinem bestehenden Metriken-Dashboard hast du damit Metriken und Logs an einem Ort.
Weiterführende Anleitungen und Quellen
- Grafana-Dashboards bauen: Datenquelle und Panel – die Basis, um die hier gezeigten Log-Panels in ein vollwertiges Dashboard zu integrieren.
- Logs lesen mit journalctl und /var/log unter Linux – für schnelles Lesen von Host-Logs direkt auf dem Server, ohne Loki.
- Zabbix-Server aufsetzen und Hosts überwachen – eine Alternative bzw. Ergänzung fürs klassische Infrastruktur-Monitoring.
- Weitere Anleitungen in der Kategorie Monitoring
Quellen: Grafana-Loki-Doku (Install with Docker / Docker Compose), Grafana-Alloy-Doku (Monitor Docker containers), Grafana-Loki-Doku (Log queries / LogQL) sowie Grafana-Loki-Doku (Log retention).