Zentrales Logging mit Loki oder dem systemd-Journal
Mache deinen Setup production-ready Teil 2
Mache deinen Setup production-ready Teil 2
Metriken sagen dir, dass etwas nicht stimmt — Logs sagen dir, warum. Wer bei einem Fehler erst per SSH auf den Server muss und sich mit docker logs durch zehn Container arbeitet, verliert wertvolle Zeit. In diesem Tutorial erweiterst du den Monitoring-Stack aus Teil 1 um zentrales Logging mit Grafana Loki: Alle Container-Logs landen durchsuchbar an einem Ort, direkt neben deinen Metriken in Grafana.
Zwei Container ergänzen den bestehenden Stack: Loki speichert Logs, Grafana Alloy sammelt sie automatisch von allen Docker-Containern ein. In Grafana durchsuchst du danach sämtliche Logs mit LogQL — inklusive Filter nach Container, Zeitraum und Fehlermeldung.
Der klassische ELK-Stack (Elasticsearch, Logstash, Kibana) ist mächtig, aber für einen einzelnen VPS überdimensioniert: Elasticsearch allein will mehrere GB RAM. Loki verfolgt einen anderen Ansatz — es indiziert nicht den kompletten Log-Inhalt, sondern nur Labels wie Container-Name und Host. Das macht es drastisch sparsamer: Der komplette Logging-Stack läuft hier mit rund 200–400 MB RAM. Und weil Grafana ohnehin schon läuft, brauchst du keine neue Oberfläche zu lernen: Logs und Metriken liegen im selben Dashboard.
Die Arbeitsteilung ist dieselbe wie bei Prometheus: Loki ist die Datenbank, Grafana Alloy ist der Collector, der die Logs einsammelt und an Loki schickt, und Grafana ist die Oberfläche zum Suchen und Visualisieren.
Ältere Anleitungen nutzen Promtail als Log-Collector. Promtail ist von Grafana offiziell abgekündigt (End of Life: März 2026) und wird durch Grafana Alloy ersetzt. Für neue Setups solltest du direkt Alloy verwenden — genau das machen wir hier.
Wechsle in dein Monitoring-Verzeichnis aus Teil 1 und lege die Konfiguration an:
Erstelle loki/loki-config.yml:
Zwei Einstellungen sind hier entscheidend: retention_period: 30d löscht Logs nach 30 Tagen automatisch (der Compactor erledigt das), und als Storage dient das lokale Dateisystem — für einen einzelnen VPS völlig ausreichend, ohne S3-Bucket oder externe Abhängigkeiten.
Alloy entdeckt über den Docker-Socket automatisch alle laufenden Container und liest deren Logs ein — ohne dass du an den bestehenden Containern irgendetwas ändern musst. Erstelle alloy/config.alloy:
Passe das host-Label an den Namen deines Servers an — sobald du später mehrere Server in dasselbe Loki schickst, unterscheidest du sie darüber.
Ergänze in deiner bestehenden ~/monitoring/docker-compose.yml zwei Services unter services::
Und unter volumes: das neue Volume:
Dann starten:
Loki braucht nach dem Start einige Sekunden, bis es bereit ist. Ein kurzer Check:
Sobald hier ready zurückkommt, nimmt Loki Logs entgegen.
Alloy liest die Logs aus den JSON-Dateien, die Docker für jeden Container schreibt — und die wachsen ohne Limit, bis die Festplatte voll ist. Da die Logs jetzt ohnehin zentral in Loki liegen, kannst du die lokalen Docker-Logs klein halten. Erstelle bzw. erweitere /etc/docker/daemon.json:
Danach sudo systemctl restart docker. Achtung: Der Neustart des Docker-Daemons startet alle Container neu — plane das entsprechend ein. Die Einstellung gilt nur für neu erstellte Container; bestehende übernehmen sie nach einem docker compose up -d --force-recreate.
Öffne Grafana und füge die neue Datenquelle hinzu:
http://loki:3100 eintragen (Container-Name, wie schon bei Prometheus)Wechsle dann zu Explore, wähle Loki als Datenquelle und probiere die erste Abfrage. LogQL funktioniert ähnlich wie PromQL — erst nach Labels filtern, dann im Text suchen:
zeigt alle Logs des Grafana-Containers. Spannender wird es mit Filtern:
durchsucht die Logs aller Container auf deinem Server nach „error". Und mit einer Rate-Funktion machst du aus Logs wieder Metriken:
zählt Fehlermeldungen pro Container über die letzten fünf Minuten — perfekt als Panel neben deinen CPU- und RAM-Graphen aus Teil 1.
Der eigentliche Gewinn zeigt sich im Zusammenspiel: Öffne dein Node-Exporter-Dashboard aus Teil 1, füge ein neues Panel hinzu, wähle als Datenquelle Loki und als Visualisierung Logs. Mit der Abfrage {host="mein-vps"} |= "error" siehst du jetzt direkt unter dem CPU-Graphen, welche Fehler zum Zeitpunkt einer Lastspitze aufgetreten sind. Genau dafür baut man zentrales Logging: Ursache und Wirkung auf einen Blick, ohne SSH-Session.
Auch Alerts auf Logs sind möglich — in Grafana unter Alerting legst du eine Regel auf eine LogQL-Abfrage an, etwa wenn die Fehlerrate eines Containers einen Schwellwert überschreitet. Die Benachrichtigung läuft dann über denselben Alertmanager bzw. dieselben Kontaktpunkte wie deine Metrik-Alerts aus Teil 1.
Bisher sammeln wir nur Container-Logs. Wer auch SSH-Logins, Kernel-Meldungen und andere System-Logs zentral haben will, ergänzt in alloy/config.alloy:
Und im Alloy-Service der Compose-Datei zwei zusätzliche Volumes:
Nach docker compose up -d --force-recreate alloy findest du die System-Logs in Grafana unter dem Label job="journal" — praktisch, um etwa fehlgeschlagene SSH-Login-Versuche im Blick zu behalten.
Loki und Alloy zusammen liegen auf einem typischen VPS bei rund 200–400 MB RAM. Der Speicherplatzbedarf hängt vom Log-Aufkommen ab: Loki komprimiert stark, ein kleiner Server mit einer Handvoll Containern bleibt mit 30 Tagen Retention meist deutlich unter 1 GB. Wächst das Log-Volumen, reduziere zuerst die retention_period, bevor du den Server vergrößerst.
Dein Server hat jetzt beides: Metriken, die dich warnen, und Logs, die dir sagen warum. Alle Container-Logs (und optional die System-Logs) sind zentral durchsuchbar, automatisch rotiert und nach 30 Tagen aufgeräumt — mit gerade einmal zwei zusätzlichen Containern.
Im nächsten Teil der Reihe „Der produktionsreife Server" vergleichen wir Nginx, Caddy und Traefik — welcher Reverse Proxy passt zu welchem Setup, und wann lohnt sich ein Wechsel?
Nein. Alloy entdeckt alle Container automatisch über den Docker-Socket und liest deren Logs ein. Weder Labels noch Logging-Driver-Änderungen an den bestehenden Containern sind nötig.
Ja. Installiere auf jedem weiteren Server nur Alloy und trage in dessen loki.write-Block die URL deines zentralen Loki ein. Wichtig: Die Verbindung per VPN, privatem Netzwerk oder Reverse Proxy mit Authentifizierung absichern, denn Loki hat mit auth_enabled: false keinen eigenen Zugriffsschutz.
Prüfe die Kette von vorne: Läuft Alloy (docker compose ps)? Meldet Loki unter /ready den Status ready? Häufigste Ursachen sind ein nicht gemounteter Docker-Socket im Alloy-Container und ein Tippfehler in der Loki-URL der Datenquelle.
VPS auf AMD EPYC mit NVMe SSD, volle Kontrolle, gehostet in Deutschland.
VPS konfigurieren
Promtail ist abgekündigt (End of Life März 2026) und wird durch Grafana Alloy ersetzt. Die Konzepte sind identisch, nur die Konfigurationssprache unterscheidet sich — neue Setups sollten direkt auf Alloy aufbauen.
cd ~/monitoring
mkdir -p loki alloyauth_enabled: false
server:
http_listen_port: 3100
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-04-01"
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index_
period: 24h
limits_config:
retention_period: 30d
compactor:
working_directory: /loki/compactor
retention_enabled: true
delete_request_store: filesystem loki:
image: grafana/loki:latest
container_name: loki
restart: unless-stopped
volumes:
- ./loki:/etc/loki
- loki-data:/loki
command:
- '-config.file=/etc/loki/loki-config.yml'
ports:
- "127.0.0.1:3100:3100"
networks:
- monitoring
alloy:
image: grafana/alloy:latest
container_name: alloy
restart: unless-stopped
volumes:
- ./alloy/config.alloy:/etc/alloy/config.alloy:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
command:
- 'run'
- '/etc/alloy/config.alloy'
networks:
- monitoringvolumes:
prometheus-data:
grafana-data:
loki-data:docker compose up -d
docker compose pscurl -s http://localhost:3100/ready{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
} - /var/log/journal:/var/log/journal:ro
- /etc/machine-id:/etc/machine-id:ro// Alle laufenden Docker-Container automatisch entdecken
discovery.docker "containers" {
host = "unix:///var/run/docker.sock"
}
// Container-Namen als sauberes Label übernehmen
discovery.relabel "containers" {
targets = discovery.docker.containers.targets
rule {
source_labels = ["__meta_docker_container_name"]
regex = "/(.*)"
target_label = "container"
}
}
// Logs der entdeckten Container einlesen
loki.source.docker "containers" {
host = "unix:///var/run/docker.sock"
targets = discovery.relabel.containers.output
labels = { host = "mein-vps" }
forward_to = [loki.write.local.receiver]
}
// An Loki weiterleiten
loki.write "local" {
endpoint {
url = "http://loki:3100/loki/api/v1/push"
}
}{container="grafana"}{host="mein-vps"} |= "error"sum by (container) (count_over_time({host="mein-vps"} |= "error" [5m]))loki.source.journal "system" {
labels = { host = "mein-vps", job = "journal" }
forward_to = [loki.write.local.receiver]
}