Monitoring-Stack: Prometheus, Grafana und Alerts per Docker einrichten
Mache deinen Setup production-ready Teil 1
Mache deinen Setup production-ready Teil 1
Ein Server ohne Monitoring ist ein Blindflug: Volle Festplatten, RAM-Engpässe oder ein abgestürzter Container fallen erst auf, wenn ein Dienst ausfällt oder schlimmer, wenn sich ein Kunde meldet. In diesem Tutorial baust du einen vollständigen Monitoring-Stack auf deinem VPS auf: Prometheus sammelt Metriken, Grafana visualisiert sie in Dashboards, und der Alertmanager schickt dir eine Benachrichtigung, bevor etwas kaputtgeht.
Fünf Container per Docker Compose starten (Prometheus, Node Exporter, cAdvisor, Grafana, Alertmanager), zwei fertige Grafana-Dashboards importieren, vier Alert-Regeln aktivieren — und du wirst per E-Mail oder Telegram gewarnt, bevor CPU, RAM oder Festplatte volllaufen.
| Komponente | Aufgabe | Port |
|---|---|---|
| Prometheus | Sammelt und speichert Metriken (Time-Series-Datenbank) | 9090 |
| Node Exporter | Liefert Host-Metriken: CPU, RAM, Disk, Netzwerk | 9100 |
| cAdvisor | Liefert Metriken pro Docker-Container | 8080 |
| Grafana | Dashboards und Visualisierung | 3000 |
| Alertmanager | Verschickt Benachrichtigungen (E-Mail, Telegram, …) | 9093 |
Das Prinzip ist einfach: Node Exporter und cAdvisor stellen Metriken als HTTP-Endpunkte bereit, Prometheus fragt („scraped") diese alle 15 Sekunden ab und speichert sie. Grafana liest aus Prometheus und zeichnet Graphen. Überschreitet ein Wert eine definierte Schwelle, feuert Prometheus eine Alert-Regel, und der Alertmanager stellt sie zu.
Am Ende sieht die Struktur so aus:
Lege in ~/monitoring die Datei docker-compose.yml an:
Zwei Details sind wichtig:
127.0.0.1 gebunden. Prometheus, Grafana und Alertmanager sind damit nicht aus dem Internet erreichbar, sondern nur lokal bzw. über einen Reverse Proxy. Wer die Weboberflächen ohne Domain nutzen will, erreicht sie per SSH-Tunnel: ssh -L 3000:localhost:3000 user@server. Eine korrekt konfigurierte Firewall mit ufw solltest du ohnehin haben.--storage.tsdb.retention.time=30d begrenzt die Speicherdauer auf 30 Tage. Auf einem VPS mit knappem Speicherplatz kannst du das reduzieren.Das Grafana-Admin-Passwort steht im Klartext in der Compose-Datei. Setze hier vor dem ersten Start ein eigenes, starkes Passwort und ändere es zusätzlich nach dem ersten Login in Grafana. Veröffentliche Grafana niemals mit Standard-Zugangsdaten hinter einer öffentlichen Domain.
Erstelle prometheus/prometheus.yml:
Da alle Container im selben Docker-Netzwerk monitoring laufen, erreichen sie sich gegenseitig über ihre Container-Namen, so braucht es keine IP-Adressen.
Erstelle prometheus/alert-rules.yml mit vier Regeln, die auf jedem Server sinnvoll sind:
Die for-Klausel verhindert Fehlalarme: Ein kurzer CPU-Spike beim Backup löst noch keinen Alert aus. Erst wenn der Zustand über den definierten Zeitraum anhält, wird der Alarm ausgelöst.
Erstelle alertmanager/alertmanager.yml. Das folgende Beispiel verschickt Alerts per E-Mail. Falls du deinen eigenen Mailserver nutzt, denke an einen korrekten PTR-Record für den Mailversand:
Alternative Telegram: Wer keinen SMTP-Zugang hat, kann Alerts direkt an einen Telegram-Bot schicken:
Einen Bot-Token bekommst du in Telegram beim @BotFather, die Chat-ID über @userinfobot.
Alle fünf Container sollten als running erscheinen. Prüfe zuerst, ob Prometheus alle Targets erreicht — per SSH-Tunnel oder Reverse Proxy unter http://localhost:9090/targets. Alle drei Jobs (prometheus, node, cadvisor) müssen den Status UP haben.
Öffne Grafana unter http://localhost:3000 (bzw. deine Domain) und melde dich mit den Zugangsdaten aus der Compose-Datei an.
Datenquelle hinzufügen:
http://prometheus:9090 eintragen (Container-Name, nicht localhost!)Dashboards importieren: Statt Dashboards selbst zu bauen, importierst du zwei bewährte Community-Dashboards über Dashboards → New → Import, indem du die jeweilige ID einträgst:
Nach dem Import wählst du jeweils deine Prometheus-Datenquelle aus, fertig! Du hast jetzt einen vollständigen Blick auf deinen Server und jeden einzelnen Container.
Wenn du unserem Caddy-Tutorial gefolgt bist, genügt ein Eintrag im Caddyfile, um Grafana per HTTPS mit automatischem Zertifikat bereitzustellen:
Danach docker compose restart caddy (bzw. systemctl reload caddy). Prometheus und Alertmanager brauchen in der Regel keine öffentliche Oberfläche — für den gelegentlichen Blick reicht der SSH-Tunnel, das reduziert die Angriffsfläche.
Vertraue keinem Alarmsystem, das du nie ausgelöst hast. Der einfachste Test ist die TargetNichtErreichbar-Regel:
Nach etwa zwei Minuten (die for-Dauer der Regel) sollte der Alert in Prometheus unter Alerts auf FIRING springen und kurz darauf die E-Mail bzw. Telegram-Nachricht eintreffen. Danach:
Der Alertmanager verschickt automatisch eine „Resolved"-Meldung.
Auf einem typischen 2-vCPU-VPS liegt der gesamte Stack bei rund 300–500 MB RAM und minimaler CPU-Last. Den größten Anteil hat cAdvisor; wer nur Host-Metriken braucht und keine Container-Details, kann ihn weglassen und spart nochmal deutlich. Der Prometheus-Speicherbedarf wächst mit der Anzahl der Metriken, mit 30 Tagen Retention und diesem Setup bleibt er meist unter 2 GB auf der Festplatte.
Mit fünf Containern und drei Konfigurationsdateien hast du jetzt ein Monitoring, das viele Ausfälle verhindert, bevor sie passieren: Du siehst Trends (die Festplatte ist in drei Wochen voll), erkennst Speicherlecks in Containern und wirst benachrichtigt, sobald ein Dienst ausfällt. Ergänze das Setup um regelmäßige Backups — Monitoring warnt dich, Backups retten dich.
Im nächsten Teil der Reihe „Der produktionsreife Server" ergänzen wir den Stack um zentrales Logging mit Loki, dann siehst du in Grafana nicht nur dass ein Container Probleme hat, sondern auch warum, direkt aus seinen Logs.
Rund 300–500 MB RAM und minimale CPU-Last. Wer cAdvisor weglässt und nur Host-Metriken sammelt, kommt mit deutlich weniger aus. Auf der Festplatte bleibt Prometheus mit 30 Tagen Retention meist unter 2 GB.
Ja. Installiere auf jedem weiteren Server nur den Node Exporter und trage ihn als zusätzliches Target in der prometheus.yml ein. Die Verbindung solltest du per privatem Netzwerk, VPN oder Firewall-Regel absichern, damit der Exporter nicht öffentlich erreichbar ist.
Prüfe zuerst unter http://localhost:9093, ob der Alert beim Alertmanager ankommt. Wenn ja, liegt es am Mailversand: SMTP-Zugangsdaten, Port 587/465 und bei eigenem Mailserver der PTR-Record sind die häufigsten Ursachen.
VPS auf AMD EPYC mit NVMe SSD, volle Kontrolle, gehostet in Deutschland.
VPS konfigurieren
Nein. Für den Alltag reicht ein SSH-Tunnel (ssh -L 3000:localhost:3000 user@server). Eine öffentliche Domain per Caddy ist bequemer, sollte dann aber mit starkem Passwort und idealerweise 2FA abgesichert sein.
monitoring/
├── docker-compose.yml
├── prometheus/
│ ├── prometheus.yml
│ └── alert-rules.yml
└── alertmanager/
└── alertmanager.ymlgrafana.deine-domain.de {
reverse_proxy localhost:3000
}mkdir -p ~/monitoring/{prometheus,grafana,alertmanager}
cd ~/monitoringservices:
prometheus:
image: prom/prometheus:latest
container_name: prometheus
restart: unless-stopped
volumes:
- ./prometheus:/etc/prometheus
- prometheus-data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.retention.time=30d'
ports:
- "127.0.0.1:9090:9090"
networks:
- monitoring
node-exporter:
image: prom/node-exporter:latest
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'
networks:
- monitoring
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
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
- /dev/disk/:/dev/disk:ro
networks:
- monitoring
grafana:
image: grafana/grafana-oss:latest
container_name: grafana
restart: unless-stopped
volumes:
- grafana-data:/var/lib/grafana
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=BitteAendern123!
- GF_SERVER_ROOT_URL=https://grafana.deine-domain.de
ports:
- "127.0.0.1:3000:3000"
networks:
- monitoring
alertmanager:
image: prom/alertmanager:latest
container_name: alertmanager
restart: unless-stopped
volumes:
- ./alertmanager:/etc/alertmanager
command:
- '--config.file=/etc/alertmanager/alertmanager.yml'
ports:
- "127.0.0.1:9093:9093"
networks:
- monitoring
volumes:
prometheus-data:
grafana-data:
networks:
monitoring:global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- "alert-rules.yml"
alerting:
alertmanagers:
- static_configs:
- targets: ["alertmanager:9093"]
scrape_configs:
- job_name: "prometheus"
static_configs:
- targets: ["localhost:9090"]
- job_name: "node"
static_configs:
- targets: ["node-exporter:9100"]
- job_name: "cadvisor"
static_configs:
- targets: ["cadvisor:8080"]groups:
- name: server-alerts
rules:
- alert: HoheCPUAuslastung
expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 90
for: 10m
labels:
severity: warning
annotations:
summary: "CPU über 90 % seit 10 Minuten ({{ $labels.instance }})"
- alert: WenigRAM
expr: (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100 < 10
for: 5m
labels:
severity: warning
annotations:
summary: "Weniger als 10 % RAM verfügbar ({{ $labels.instance }})"
- alert: FestplatteFastVoll
expr: (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) * 100 < 15
for: 5m
labels:
severity: critical
annotations:
summary: "Weniger als 15 % Speicherplatz auf / frei ({{ $labels.instance }})"
- alert: TargetNichtErreichbar
expr: up == 0
for: 2m
labels:
severity: critical
annotations:
summary: "Exporter oder Dienst nicht erreichbar: {{ $labels.job }}"global:
smtp_smarthost: "mail.deine-domain.de:587"
smtp_from: "[email protected]"
smtp_auth_username: "[email protected]"
smtp_auth_password: "dein-smtp-passwort"
route:
receiver: "mail"
group_by: ["alertname"]
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
receivers:
- name: "mail"
email_configs:
- to: "[email protected]"receivers:
- name: "telegram"
telegram_configs:
- bot_token: "123456:ABC-DEIN-BOT-TOKEN"
chat_id: -100123456789
parse_mode: "HTML"cd ~/monitoring
docker compose up -d
docker compose psdocker stop node-exporterdocker start node-exporter