Docker Logging & Monitoring 2026 – Loki, Promtail & Grafana

Warum Docker-Logging 2026 neu gedacht werden muss

Der Standard-Logging-Treiber von Docker ist seit Jahren json-file – und er hat ein strukturelles Problem: Er schreibt unbegrenzt. Ohne max-size und max-file wächst eine einzelne Container-Logdatei so lange, bis die Partition voll ist. Bei einem typischen Node.js- oder Python-Service mit Request-Logging sind 20 MB pro Tag und Container keine Seltenheit, bei einem geschwätzigen Java-Service mit Log4j sind 200 MB pro Tag realistisch.

Rechnen wir das hoch: 50 Container × 20 MB/Tag ergeben 1 GB pro Tag, also rund 365 GB pro Jahr. Auf einem VPS mit 160 GB NVMe ist die Platte nach fünf Monaten voll – und dann startet der Container nicht mehr, weil Docker die Logdatei nicht mehr schreiben kann. Genau dieses Szenario ist 2026 immer noch einer der häufigsten Gründe für nächtliche Pager-Alarme.

Dazu kommt das zweite Problem: docker logs funktioniert nur lokal und nur pro Container. Sobald du einen Request über vier Microservices verfolgen willst, stehst du vor vier SSH-Sessions und suchst manuell nach einer Correlation-ID. Bei einem Incident mit 15 Minuten Downtime kostet das schnell mehr als ein ganzer Monat Hosting.

Die Lösung ist ein zentraler Log-Stack, der Logs sammelt, indexiert, durchsuchbar macht und Alarme auslöst. 2026 hat sich dafür der Stack aus Grafana Loki, Promtail (bzw. dem Nachfolger Grafana Alloy) und Grafana als De-facto-Standard etabliert – vor allem, weil er deutlich günstiger zu betreiben ist als ELK.

Loki vs. ELK: Warum der schlanke Stack gewinnt

Der entscheidende Unterschied liegt im Index. Elasticsearch indexiert jedes Wort jeder Logzeile – das ermöglicht Volltextsuche, kostet aber massiv Speicher und RAM. Loki indexiert ausschließlich die Labels (also Container-Name, Service, Namespace, Host) und speichert den Log-Inhalt als komprimierte Chunks. Die Suche läuft dann per Brute-Force-Scan über die passenden Chunks.

In der Praxis bedeutet das: Ein Elasticsearch-Cluster für 50 GB Logs pro Monat braucht typischerweise 16–32 GB RAM und 500 GB bis 1 TB Storage. Loki mit gleicher Logmenge läuft auf 4–8 GB RAM und 100–150 GB Storage, weil die Chunks mit Snappy bzw. Zstd komprimiert werden und im Schnitt eine Kompressionsrate von 8:1 bis 12:1 erreicht wird.

KriteriumGrafana LokiElasticsearch / ELK
Index-StrategieNur LabelsVolltext (invertierter Index)
RAM-Bedarf (50 GB/Monat)4–8 GB16–32 GB
Storage-Bedarf100–150 GB500 GB–1 TB
AbfragespracheLogQLKQL / Lucene DSL
BetriebsaufwandNiedrig (Single Binary möglich)Hoch (Cluster, Shards, ILM)
Kosten VPS/Monatab ca. 8 €ab ca. 40 €

Der Nachteil: Loki ist bei ungezielten Volltextsuchen über Terabytes langsamer. Für den typischen DevOps-Alltag – „zeig mir alle Errors von Service X in den letzten 30 Minuten“ – ist das irrelevant. Wer wirklich Volltext-Forensik über Jahre braucht, fährt mit ELK oder OpenSearch besser.

Die Architektur: Promtail, Loki und Grafana im Zusammenspiel

Der Stack besteht aus drei Komponenten mit klar getrennten Aufgaben. Promtail läuft als Agent auf jedem Docker-Host, liest die Logdateien aus /var/lib/docker/containers/ und schickt sie mit Labels an Loki. Loki nimmt die Streams entgegen, komprimiert sie, speichert sie als Chunks und hält einen kleinen Label-Index. Grafana ist die Abfrage- und Visualisierungsschicht.

Wichtige Ports für die Firewall-Konfiguration:

Seit 2024 empfiehlt Grafana offiziell Grafana Alloy als Nachfolger von Promtail, da Promtail in den Maintenance-Modus übergegangen ist. Die Konfiguration ist bei Alloy in River-Syntax statt YAML geschrieben. Promtail funktioniert 2026 weiterhin einwandfrei und ist für Docker-only-Setups einfacher – wir zeigen deshalb beide Wege, konzentrieren uns aber auf Promtail, weil die Docker-Service-Discovery dort deutlich kompakter ist.

Docker Compose: Der komplette Stack in einer Datei

Das folgende Setup läuft auf einem VPS mit 4 GB RAM problemlos. Wichtig ist, dass Loki und Promtail im selben Docker-Netzwerk liegen und die Logs über ein benanntes Volume persistent bleiben.

services:
  loki:
    image: grafana/loki:3.4.1
    container_name: loki
    restart: unless-stopped
    command: -config.file=/etc/loki/loki-config.yaml
    volumes:
      - ./loki-config.yaml:/etc/loki/loki-config.yaml:ro
      - loki-data:/loki
    ports:
      - "127.0.0.1:3100:3100"
    networks: [monitoring]

  promtail:
    image: grafana/promtail:3.4.1
    container_name: promtail
    restart: unless-stopped
    command: -config.file=/etc/promtail/promtail-config.yaml
    volumes:
      - ./promtail-config.yaml:/etc/promtail/promtail-config.yaml:ro
      - /var/lib/docker/containers:/var/lib/docker/containers:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks: [monitoring]

  grafana:
    image: grafana/grafana:11.5.0
    container_name: grafana
    restart: unless-stopped
    environment:
      GF_SECURITY_ADMIN_PASSWORD: "ChAng3M3-Str0ng!"
      GF_USERS_ALLOW_SIGN_UP: "false"
    volumes:
      - grafana-data:/var/lib/grafana
    ports:
      - "3000:3000"
    networks: [monitoring]

volumes:
  loki-data:
  grafana-data:

networks:
  monitoring:
    driver: bridge

Start mit docker compose up -d. Danach erreichst du Grafana unter http://SERVER-IP:3000. Der Docker-Socket wird read-only gemountet – das ist Pflicht, denn ein beschreibbarer Socket-Mount ist gleichbedeutend mit Root-Zugriff auf den Host.

Achte darauf, dass Loki nur auf 127.0.0.1 lauscht. Wenn du Loki öffentlich erreichbar machst, kann jeder mit der Push-API deinen Storage fluten – ein klassischer Denial-of-Wallet-Angriff bei Cloud-Storage-Backends.

Loki konfigurieren: Retention, Compactor und Storage

Die wichtigste Einstellung ist die Retention. Ohne sie wächst Loki unbegrenzt. Für die meisten Setups sind 30 Tage ein guter Kompromiss zwischen Debugging-Fähigkeit und Kosten.

auth_enabled: false

server:
  http_listen_port: 3100
  grpc_listen_port: 9096
  log_level: warn

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

limits_config:
  retention_period: 720h
  ingestion_rate_mb: 10
  ingestion_burst_size_mb: 20
  max_query_series: 5000
  reject_old_samples: true
  reject_old_samples_max_age: 168h

compactor:
  working_directory: /loki/compactor
  retention_enabled: true
  delete_request_store: filesystem
  compaction_interval: 10m

retention_period: 720h entspricht 30 Tagen. Der Compactor löscht abgelaufene Chunks alle 10 Minuten. Ohne aktivierten Compactor wird die Retention-Einstellung schlicht ignoriert – ein häufiger Konfigurationsfehler.

Für produktive Setups mit mehr als 100 GB Logvolumen lohnt sich Object Storage. S3-kompatibel sind Hetzner Object Storage (1 TB ab ca. 5 €/Monat), Backblaze B2 (6 $/TB/Monat) oder Wasabi (6,99 $/TB/Monat). Der Wechsel erfolgt über einen s3-Block in der storage_config mit endpoint, bucketnames, access_key_id und secret_access_key.

Promtail konfigurieren: Docker Service Discovery

Promtail kann Container automatisch über den Docker-Socket entdecken. Das ist der eleganteste Weg, weil neue Container ohne Neustart des Agents erfasst werden.

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: 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_log_stream']
        target_label: stream
      - source_labels: ['__meta_docker_container_label_com_docker_compose_service']
        target_label: service
      - source_labels: ['__meta_docker_container_label_com_docker_compose_project']
        target_label: project
    pipeline_stages:
      - docker: {}
      - match:
          selector: '{service="nginx"}'
          stages:
            - regex:
                expression: '^(?P<ip>\S+) - - \[(?P<ts>[^\]]+)\] "(?P<method>\S+) (?P<path>\S+)'
            - labels:
                method:
                path:

Der docker: {}-Stage ist entscheidend: Er parst das JSON-Format von Docker und extrahiert den eigentlichen Log-Text sowie den Zeitstempel. Ohne ihn landen die Logs mit dem JSON-Wrapper in Loki und sind praktisch unlesbar.

Die labels-Direktive im Regex-Stage erzeugt neue Labels aus den extrahierten Feldern. Achtung: Jedes zusätzliche Label mit hoher Kardinalität (z. B. Request-IDs oder User-IDs) sprengt den Loki-Index. Halte Labels bei unter 100.000 Kombinationen.

LogQL in der Praxis: Die 12 Queries, die du wirklich brauchst

LogQL kombiniert Label-Filter (schnell, indexiert) mit Line-Filtern (langsam, aber flexibel). Die Grundregel: So viele Filter wie möglich in den Label-Selektor, so wenige wie nötig in die Pipeline.

# Alle Logs eines Service
{service="api"} 

# Nur Fehler
{service="api"} |= "ERROR"

# Fehler ausschließen
{service="api"} != "healthcheck"

# Regex-Filter
{service="nginx"} |~ "5[0-9]{2} "

# JSON parsen und filtern
{service="api"} | json | level="error" | duration_ms > 1000

# Fehlerrate pro Minute
sum(count_over_time({service="api"} |= "ERROR" [1m]))

# Top 5 Container nach Logvolumen
topk(5, sum by (container) (rate({job="docker"}[5m])))

# Langsamste Requests
{service="api"} | json | unwrap duration_ms | quantile_over_time(0.99, {} [5m])

Der unwrap-Operator ist der wichtigste für Metriken aus Logs: Er wandelt einen numerischen Wert aus einer Logzeile in eine Zeitreihe um. Damit baust du Latenz-Perzentile direkt aus Logs, ohne dass die Anwendung Prometheus-Metriken exportieren muss.

Für die Fehlersuche über Services hinweg nutzt du eine Correlation-ID. Voraussetzung ist, dass alle Services sie loggen:

{job="docker"} |= "req-8f3a9c21"

Diese Query liefert alle Logzeilen aller Container, die diese Request-ID enthalten – sortiert nach Zeit. Aus 15 Minuten manueller Suche werden 3 Sekunden.

Grafana: Dashboards, Datenquelle und Alerting

Nach dem Login unter http://SERVER-IP:3000 (Standard: admin / das gesetzte Passwort) fügst du unter Connections → Data Sources eine Loki-Quelle mit der URL http://loki:3100 hinzu. Da beide im selben Docker-Netzwerk liegen, funktioniert der Container-Name als Hostname.

Für ein solides Basis-Dashboard brauchst du vier Panels:

  1. Log-Volumen pro Service – sum by (service) (rate({job="docker"}[5m])) als Time Series
  2. Fehlerrate – sum(count_over_time({job="docker"} |= "ERROR" [1m])) als Stat
  3. Live-Logs – Logs-Panel mit {service="api"}
  4. Top-Fehlerquellen – topk(5, sum by (container) (count_over_time({job="docker"} |= "ERROR" [1h])))

Für Alerting definierst du in Grafana eine Alert Rule auf Basis einer LogQL-Metrik-Query. Ein praxistauglicher Alert: Fehlerrate über 10 Events pro Minute für 5 Minuten.

sum(count_over_time({job="docker"} |= "ERROR" [1m])) > 10

Als Contact Point reicht ein Webhook auf Slack oder Discord, oder Mail über einen SMTP-Relay. Grafana 11 bringt Alertmanager direkt mit – ein separater Prometheus-Alertmanager ist für diesen Stack nicht nötig. Setze die group_wait-Zeit auf 30 s und repeat_interval auf 4 h, sonst wird dein Team in der Nacht zugespammt.

Metriken dazu: cAdvisor, node-exporter und Prometheus

Logs allein reichen nicht. Wenn ein Container 100 % CPU zieht, willst du das sehen, bevor die Logs vollaufen. Ergänze den Stack um drei Komponenten:

  cadvisor:
    image: gcr.io/cadvisor/cadvisor:v0.50.0
    container_name: cadvisor
    restart: unless-stopped
    privileged: true
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:ro
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro
    ports:
      - "127.0.0.1:8080:8080"
    networks: [monitoring]

  node-exporter:
    image: prom/node-exporter:v1.8.2
    container_name: node-exporter
    restart: unless-stopped
    pid: host
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    command:
      - '--path.procfs=/host/proc'
      - '--path.sysfs=/host/sys'
      - '--path.rootfs=/rootfs'
    ports:
      - "127.0.0.1:9100:9100"
    networks: [monitoring]

cAdvisor braucht privileged: true – das ist ein Sicherheitskompromiss. Wer das vermeiden will, nutzt stattdessen den Docker-Daemon-Metriken-Endpunkt oder setzt spezifischere Capabilities. Für ein dediziertes Monitoring-VPS ist privileged vertretbar, auf einem Shared-Host nicht.

Sinnvolle Alerts auf Metrik-Basis: Container-Restart-Loop (rate(container_restart_count[10m]) > 0), RAM-Auslastung über 90 % für 10 Minuten, Disk-Auslastung über 85 %, und Host-Load über der doppelten CPU-Kernzahl.

Ressourcenbedarf und Kosten: Was der Stack wirklich kostet

Die realistische Verbrauchsmessung eines Stacks mit 30 Containern und rund 2 GB Logs pro Tag:

KomponenteRAMCPUDisk (30 Tage)
Loki300–600 MB0,2–0,5 vCPUca. 7 GB (komprimiert)
Promtail40–80 MB0,05 vCPU–
Grafana150–250 MB0,1 vCPU1–2 GB
Prometheus400–800 MB0,3 vCPU5–10 GB
cAdvisor80–150 MB0,1 vCPU–
Gesamtca. 1,2–2 GBca. 1 vCPUca. 15–20 GB

Damit reicht ein VPS mit 4 GB RAM und 80 GB NVMe. Passende Angebote (Stand 2026, Preise gerundet):

AnbieterModellSpecsPreis/Monat
HetznerCX222 vCPU, 4 GB, 40 GBca. 4,35 €
HetznerCX324 vCPU, 8 GB, 80 GBca. 7,60 €
NetcupVPS 2000 G114 vCPU, 8 GB, 160 GBca. 8,99 €
ContaboVPS S4 vCPU, 8 GB, 200 GBca. 5,99 €
IONOSVPS Linux L4 vCPU, 8 GB, 160 GBca. 12,00 €

Für die 40-GB-Variante bei Hetzner reicht der Platz für etwa drei Monate Retention. Wer ein Jahr aufbewahren will, nimmt Object Storage dazu: 1 TB bei Hetzner kostet rund 5 €/Monat inklusive Traffic. Damit liegt der komplette Monitoring-Stack bei unter 15 € monatlich – im Vergleich zu einem Elasticsearch-Cluster mit 40–80 € eine klare Ersparnis.

Sicherheit und Best Practices

Der Docker-Socket ist der kritischste Punkt im gesamten Stack. Ein beschreibbarer Mount erlaubt das Starten privilegierter Container und damit Root-Zugriff auf den Host. Setze immer :ro und ziehe zusätzlich den Socket-Proxy tecnativa/docker-socket-proxy in Betracht, der nur die benötigten API-Endpunkte freigibt.

Weitere Absicherungen:

Die doppelte Absicherung über Docker-Log-Rotation ist wichtig: Wenn Loki ausfällt, laufen die Container trotzdem weiter und die Logs bleiben lokal begrenzt. Ohne diese Rotation füllt sich die Platte innerhalb von Stunden.

Für die Aufbewahrung personenbezogener Daten gilt die DSGVO: Logs mit IP-Adressen oder User-IDs sollten nach 7 bis 14 Tagen gelöscht werden, sofern keine berechtigte Aufbewahrungspflicht besteht. Setze dafür pro Stream unterschiedliche Retention-Periods über limits_config und Stream-Matcher.

Troubleshooting: Die fünf häufigsten Fehler

1. „entry too far behind“ beim Push. Loki lehnt Logs ab, die älter als reject_old_samples_max_age sind. Ursache ist meist ein falsch geparster Zeitstempel. Prüfe, ob der docker: {}-Stage aktiv ist.

2. Keine Logs in Grafana, aber Promtail läuft. Prüfe die Position-Datei unter /tmp/positions.yaml. Wurde sie gelöscht oder ist der Container neu gestartet, liest Promtail alles neu ein – oder überspringt bereits gelesene Dateien. Bei einem Wechsel von json-file auf einen anderen Log-Treiber findet Promtail gar nichts mehr.

3. Loki startet nicht mit „too many open files“. Erhöhe LimitNOFILE in der systemd-Unit oder setze ulimits im Compose-File auf nofile: 65536.

4. Grafana zeigt „Data source connected, but no labels“. Meist ein Netzwerkproblem – Grafana erreicht http://loki:3100 nicht, weil der Container in einem anderen Docker-Netzwerk hängt. Prüfe mit docker exec grafana wget -qO- http://loki:3100/ready.

5. Hohe Kardinalität. Wenn Loki plötzlich 8 GB RAM zieht und Queries langsam werden, hast du zu viele Labels. Prüfe mit curl -s http://localhost:3100/loki/api/v1/labels und entferne alles, was pro Request einen neuen Wert annimmt.

Ein nützlicher Health-Check für den Betrieb: curl -s http://localhost:3100/ready sollte ready zurückgeben, und curl -s http://localhost:3100/metrics | grep loki_ingester_streams zeigt die Anzahl aktiver Streams. Über 10.000 Streams auf einem kleinen VPS sind ein Warnsignal.

FAQ

Brauche ich Promtail oder soll ich direkt Grafana Alloy nutzen?

Für ein reines Docker-Setup ist Promtail 2026 weiterhin die einfachere Wahl, weil die Konfiguration kompakt in YAML bleibt und die Docker-Service-Discovery direkt eingebaut ist. Grafana Alloy ist der offizielle Nachfolger und lohnt sich, wenn du Logs, Metriken und Traces in einem einzigen Agenten bündeln willst. Alloy nutzt die River-Syntax, die sich deutlich von Promtail-YAML unterscheidet, und kann bestehende Promtail-Konfigurationen per promtail.convert-Befehl importieren. Für neue Setups mit mehr als drei Hosts würde ich direkt auf Alloy setzen, für Einzelhost-Setups bleibt Promtail pragmatischer.

Wie viel Speicher brauche ich wirklich für 30 Tage Logs?

Rechne mit der Rohmenge geteilt durch 8 bis 12. Bei 2 GB Logs pro Tag sind das 60 GB Rohdaten pro Monat und etwa 5 bis 7,5 GB komprimierte Chunks in Loki. Dazu kommt der Label-Index mit rund 1 bis 2 Prozent der Rohmenge. Für 30 Tage bei 2 GB/Tag brauchst du also etwa 8 bis 10 GB Storage. Bei 10 GB Logs pro Tag entsprechend 40 bis 50 GB. Object Storage ist ab etwa 50 GB Logvolumen pro Monat günstiger als Block Storage.

Kann ich Loki auch ohne Docker betreiben?

Ja. Loki wird als einzelne Binary ausgeliefert und lässt sich per systemd betreiben. Der Vorteil im Container ist die einfachere Aktualisierung und die isolierte Konfiguration. Wenn du Loki nativ betreibst, achte auf ausreichende nofile-Limits und darauf, dass der Nutzer Schreibrechte auf /loki hat. Für die meisten Setups ist Docker Compose aber der schnellere Weg, weil Loki, Grafana und Prometheus dann gemeinsam im gleichen Netzwerk laufen und du keine Ports nach außen öffnen musst.

Was kostet der komplette Stack im Monat?

Auf einem Hetzner CX32 mit 4 vCPU, 8 GB RAM und 80 GB NVMe liegst du bei rund 7,60 € monatlich für den Server. Ein Domainname kostet etwa 1 € pro Monat, TLS über Let's Encrypt ist kostenlos. Mit Object Storage für ein Jahr Retention kommen rund 5 € pro TB dazu. In Summe sind das 8 bis 15 € monatlich – deutlich weniger als ein vergleichbarer Elasticsearch-Cluster, der bei gleicher Logmenge mindestens 40 € an Serverkosten verursacht.

Wie sichere ich Loki gegen Datenverlust ab?

Zum einen über Object Storage mit aktivierter Versionierung, zum anderen über regelmäßige Snapshots des VPS. Der Loki-Index ist reproduzierbar, die Chunks nicht – verlierst du die Chunks, sind die Logs weg. Aktiviere deshalb entweder S3-Backend mit Versionierung oder erstelle mindestens täglich einen Snapshot des loki-data-Volumes. Für den Compactor gilt: delete_request_store muss auf denselben Storage-Typ zeigen wie die Chunks, sonst schlägt das Löschen abgelaufener Daten fehl und die Retention greift nicht.