Logs zentralisieren mit Grafana Loki
So sammeln Sie alle Logs Ihrer 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.
Geprüft am 09.10.2026 · für loki 3.7.8, alloy 1.20.1, grafana 13.2.3
WerbelinksMit * markierte Links sind Werbelinks: Bei einem Kauf erhalten wir eine Provision, der Preis bleibt gleich. Als Amazon-Partner verdiene ich an qualifizierten Verkäufen. Mehr dazu

Wer mehrere Linux-Server und Docker-Container betreibt, meldet sich bei einem Fehler per SSH auf jeder Maschine an und sucht einzeln in journalctl und Logdateien. Logs zentralisieren mit Grafana Loki bündelt alle Logs an einem Ort, self-hosted in Docker. Diese Anleitung baut einen kompakten Stack aus Loki, Grafana Alloy und Grafana auf und zeigt die wichtigsten LogQL-Abfragen.
Voraussetzungen
- Ein Linux-Server (x86-64 oder ARM64) mit Docker und Docker Compose v2. Für einen einzelnen Host reichen 2 CPU-Kerne und 4 GB RAM.
- Ein Benutzer mit Rechten, den Docker-Daemon zu nutzen, und Lesezugriff auf den Docker-Socket
/var/run/docker.sock(für Container-Logs). - Plattenplatz für die Log-Chunks im Loki-Volume, abhängig von Log-Menge und Retention.
- 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.
Die Anleitung baut einen monolithischen Single-Node-Stack (Loki mit target: all und Filesystem-Storage). Im Test lief der Stack mit Loki 3.7.8, Alloy v1.20.1 und Grafana 13.2.3: Loki meldete „ready“, Alloy lieferte die Container-Logs mit den Labels job und container, und Grafana verband sich über die provisionierte Datenquelle.
Schritt 1: Architektur verstehen
Der Stack besteht aus drei getrennten Rollen:
| 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 |
Alloy schickt die Logs an den Push-Endpunkt von Loki, im Compose-Netz http://loki:3100/loki/api/v1/push. Loki legt die Daten ab, Grafana liest sie aus.
Loki indexiert nur Labels (z. B. job, container, level), den Log-Text durchsuchen Sie zur Abfragezeit mit Zeilenfiltern. Halten Sie deshalb die Label-Kardinalität (Zahl der unterschiedlichen Label-Werte) niedrig: Request-IDs, IP-Adressen oder Zeitstempel gehören nicht in Labels. Der frühere Collector Promtail ist End-of-Life (siehe Häufige Fragen).
Verifizieren: Sie können für jede der drei Komponenten Rolle und Port benennen und wissen, dass Alloy an http://loki:3100/loki/api/v1/push sendet.
Schritt 2: Docker-Compose-Stack anlegen
Legen Sie ein Projektverzeichnis an, z. B. /opt/loki-stack, und darin eine compose.yaml:
services:
loki:
image: grafana/loki:3.7.8
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.20.1
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:13.2.3
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: {}
Die Image-Tags sind auf feste Versionen gesetzt statt :latest, Loki und Alloy binden ihre Ports nur an 127.0.0.1, und Alloy erhält den Docker-Socket read-only. Ohne diesen Mount sieht Alloy keine Container-Logs. Ersetzen Sie bitte-aendern durch ein eigenes Passwort.
Verifizieren: docker compose config --quiet im Projektverzeichnis endet ohne Ausgabe. Gestartet wird der Stack in Schritt 5, wenn alle Konfigurationsdateien vorliegen.
Schritt 3: loki-config.yaml schreiben
Diese loki-config.yaml läuft monolithisch mit Filesystem-Storage und aktiviert bereits die Retention (Details 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
auth_enabled: false bedeutet: Loki hat keinen Zugriffsschutz (siehe Schritt 7). Die index.period muss 24h betragen, sonst greift die Retention nicht. retention_period legt die Aufbewahrung fest, ohne diesen Wert (Default 0s) wachsen die Logs unbegrenzt.
Verifizieren: Nach dem Start in Schritt 5 enthält docker compose logs loki die Zeile msg=starting module=compactor und keine Konfigurationsfehler.
Schritt 4: Alloy als Collector konfigurieren
Die Datei config.alloy nutzt die Alloy-eigene Syntax. Die Pipeline entdeckt alle Docker-Container, setzt den Container-Namen als Label und schickt die 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"
}
}
Nach dem Start liegen alle Container-Logs unter {job="docker"} und je Container unter {container="..."}. Die Alloy-Web-UI unter http://localhost:12345 zeigt den Zustand der Pipeline-Komponenten.
Für Host-Logs ergänzen Sie eine loki.source.journal-Quelle bzw. local.file_match auf /var/log/syslog und mounten /var/log/journal bzw. /var/log read-only in den Alloy-Container. Ohne Mount kommen keine Host-Logs an.
Verifizieren: Nach dem Start liefert curl -s http://localhost:3100/loki/api/v1/label/container/values die Namen Ihrer Container, im Test alloy, grafana und loki.
Schritt 5: Loki als Datenquelle in Grafana
Richten Sie die Datenquelle per Provisioning ein. Legen Sie im Projektverzeichnis eine grafana-datasources.yaml an, die Compose nach /etc/grafana/provisioning/datasources/ mountet:
apiVersion: 1
datasources:
- name: Loki
type: loki
access: proxy
url: http://loki:3100
isDefault: false
Starten Sie den Stack und prüfen Sie Loki:
cd /opt/loki-stack
docker compose up -d
# Health-Check: liefert nach etwa 15 Sekunden "ready"
curl http://localhost:3100/ready
# Laufen alle drei Container?
docker compose ps
Alternativ richten Sie die Datenquelle manuell ein: Grafana unter http://localhost:3000 öffnen (Login admin mit Ihrem Passwort), dann:
- Connections → Add new connection
- Den Typ Loki auswählen und Add new data source klicken
- Bei URL
http://loki:3100eintragen - Unten auf Save & test klicken
Verifizieren: docker compose ps zeigt loki, alloy und grafana als „Up“, curl http://localhost:3100/ready antwortet ready, und Save & test meldet „Data source successfully connected.“
Schritt 6: LogQL-Grundlagen und Explore
LogQL ist an PromQL angelehnt. Pflicht ist ein Label-Selektor in geschweiften Klammern, danach optional eine Pipeline. Abfragen testen Sie in Explore (linkes Menü, Datenquelle Loki). Die 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
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 brauchen Sie Metrik-Queries, die Logzeilen in Zahlenreihen umwandeln:
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
Ein Selektor mit Zeilenfilter liefert Logzeilen (Logs-Panel, Explore), rate(), count_over_time() oder sum by() liefern eine Metrik für ein Graph-Panel.
Verifizieren: {job="docker"} zeigt in Explore Logzeilen, sum by (container) (count_over_time({job="docker"} |= "error" [5m])) eine Zahl je Container.
Schritt 7: Log-Dashboard bauen und Loki absichern
Aus Explore heraus bauen Sie 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
Zur Sicherheit: Loki bringt keine Authentifizierung mit. Port 3100 darf nie ungeschützt ins Netz. Setzen Sie 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 sind beide Ports nur an
127.0.0.1gebunden. - Ein starkes Grafana-Admin-Passwort statt des Defaults
admin.
Verifizieren: Das Dashboard zeigt beide Panels nach dem Neuladen, docker compose ps listet 3100 und 12345 nur mit 127.0.0.1, und von einem anderen Rechner ist Port 3100 nicht erreichbar.
Schritt 8: Retention und Limits richtig setzen
Ohne Retention wachsen die Logs unbegrenzt. Die nötigen Einstellungen sind in der loki-config.yaml aus Schritt 3 schon gesetzt:
| 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 |
Für unterschiedliche Aufbewahrung je Quelle definieren Sie eine Stream-Retention:
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
Gelöschte Chunks geben den Platz erst nach retention_delete_delay frei. max_query_lookback sollte höchstens so groß wie retention_period sein.
Verifizieren: Nach docker compose restart loki enthält docker compose logs loki keine Fehler zu retention oder delete_request_store, sondern wieder msg=starting module=compactor.
Troubleshooting / Typische Fehler
- Keine Container-Logs in Loki: Mount
/var/run/docker.sockim Alloy-Container prüfen, ebensodocker 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 oder Retention greift nicht: Einstellungen aus der Tabelle in Schritt 8 prüfen, insbesondere
index.period: 24h. - Loki langsam oder überlastet: Meist zu hohe Label-Kardinalität. Details wie Request-IDs über Zeilenfilter und
| json/| logfmtabfragen. - Explore zeigt nichts an: Log- und Metrik-Query verwechselt (siehe Schritt 6)?
- Alloy meldet
timestamp too old: Beim ersten Start liest Alloy auch ältere Logs bestehender Container. Loki verwirft Einträge, die älter als eine Woche sind (reject_old_samples_max_age). Die Meldung verschwindet danach, neue Logs kommen an. - Upgrade kaputt: Feste Versionen wie
grafana/loki:3.7.8statt:latestverwenden.
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 erhält keine Updates oder Sicherheitskorrekturen mehr. Bestehende Promtail-Konfigurationen wandelt alloy convert --source-format=promtail in Alloy-Syntax um.
Indexiert Loki den gesamten Log-Text?
Nein. Loki indexiert nur die Labels und speichert den Text als komprimierte Chunks. Den Volltext durchsuchen Sie zur Abfragezeit über Zeilenfilter und Parser. Deshalb braucht Loki vergleichsweise wenig Speicher.
Wie sammle ich Host-Logs wie journald oder /var/log/syslog ein?
Wie in Schritt 4 beschrieben, über loki.source.journal bzw. local.file_match plus read-only-Mount. Für schnelles Lesen direkt auf dem Host bleibt journalctl die einfachste Wahl.
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). Ein monolithisches Loki mit Filesystem-Storage wie in dieser Anleitung reicht.
Ist mein Loki ohne weitere Maßnahmen sicher erreichbar?
Nein. Loki hat keine Authentifizierung. Schützen Sie Port 3100 wie in Schritt 7 beschrieben.
Warum sinkt mein Plattenplatz nach dem Setzen der Retention nicht sofort?
Gelöschte Chunks werden erst nach retention_delete_delay (Default 2 Stunden) entfernt, und der Compactor läuft nur alle compaction_interval (hier 10 Minuten).
Fazit
Loki, Grafana Alloy und Grafana bilden eine schlanke, self-hosted Log-Zentrale: Alloy sammelt, Loki speichert und indexiert nur Labels, Grafana fragt per LogQL ab. Halten Sie die Label-Kardinalität niedrig, stellen Sie Loki nie offen ins Netz und aktivieren Sie die Retention. Dann reicht der Stack vom einzelnen Server bis zur kleinen KMU-Umgebung.
Weiterführende Anleitungen und Quellen
- Grafana-Dashboards bauen: Datenquelle und Panel – Log-Panels in ein Dashboard einbauen.
- Logs lesen mit journalctl und /var/log unter Linux – Host-Logs direkt auf dem Server lesen.
- Zabbix-Server aufsetzen und Hosts überwachen – klassisches Infrastruktur-Monitoring.
- Weitere Anleitungen in der Kategorie Monitoring
Quellen: Grafana-Loki-Doku (Install with Docker Compose, LogQL, Log retention), Grafana-Alloy-Doku (Monitor Docker containers).


