Grafana and Prometheus are the pairing people reach for once they have more than one server and no real way to answer "is it actually fine, or does it just look fine right now" — a dashboard you own, built from metrics you scraped yourself, instead of a monitoring bill that grows with every host added.Grafana und Prometheus sind das Duo, zu dem Leute greifen, sobald sie mehr als einen Server haben und keine echte Möglichkeit, die Frage zu beantworten „ist es wirklich in Ordnung, oder sieht es gerade nur so aus“ – ein Dashboard, das dir gehört, gebaut aus Metriken, die du selbst gescrapt hast, statt einer Monitoring-Rechnung, die mit jedem hinzugefügten Host wächst.
Three jobs, three containers:Drei Aufgaben, drei Container:
/metrics endpoints you listed, stores every sample as a time series on local disk, and answers queries against that history. Its own built-in UI is for debugging queries, not a dashboard worth using day to day.Prometheus erledigt die eigentliche Arbeit: per Timer scrapt es die /metrics-Endpunkte, die du eingetragen hast, speichert jeden Sample als Zeitreihe auf lokaler Platte und beantwortet Abfragen gegen diese Historie. Seine eigene eingebaute UI ist zum Debuggen von Queries gedacht, nicht als Dashboard für den täglichen Gebrauch.Exporter exposes, Prometheus scrapes and stores, Grafana draws — nothing pushes metrics anywhere, everything gets pulled by Prometheus on its own schedule.Exporter stellt bereit, Prometheus scrapt und speichert, Grafana zeichnet — nichts pusht Metriken irgendwohin, alles wird von Prometheus nach eigenem Zeitplan abgeholt.
services:
node-exporter:
image: prom/node-exporter:v1.8.2 # check hub.docker.com/r/prom/node-exporter/tags for the current release
restart: unless-stopped
pid: host
network_mode: host
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro,rslave
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- '--path.rootfs=/rootfs'
- '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
prometheus:
image: prom/prometheus:v2.54.1 # check hub.docker.com/r/prom/prometheus/tags for the current release
restart: unless-stopped
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus-data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--storage.tsdb.retention.time=15d'
ports:
- "127.0.0.1:9090:9090"
grafana:
image: grafana/grafana-oss:11.1.4 # check hub.docker.com/r/grafana/grafana-oss/tags for the current release
restart: unless-stopped
volumes:
- grafana-data:/var/lib/grafana
ports:
- "127.0.0.1:3000:3000"
volumes:
prometheus-data:
grafana-data:
node-exporter uses network_mode: host so it can see the host's own network interfaces and real filesystem mount points rather than the container's own — the documented way to run it, not a shortcut. That also means it sits outside compose's usual port mapping: whatever binds to 9100 on the host is 9100, which matters below. Prometheus and Grafana both publish only to 127.0.0.1 — nothing but a reverse proxy on the same host should reach either one directly.node-exporter verwendet network_mode: host, damit es die echten Netzwerk-Interfaces und Dateisystem-Mountpoints des Hosts sieht statt die eigenen des Containers — das ist die dokumentierte Art, es zu betreiben, keine Abkürzung. Das bedeutet auch, dass es außerhalb von Composes üblichem Port-Mapping steht: Was auch immer sich auf dem Host an 9100 bindet, ist 9100, was weiter unten wichtig wird. Prometheus und Grafana veröffentlichen beide nur auf 127.0.0.1 — nichts außer einem Reverse-Proxy auf demselben Host sollte einen von beiden direkt erreichen.
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'node-local'
static_configs:
- targets: ['host.docker.internal:9100']
- job_name: 'node-remote'
static_configs:
- targets:
- '10.10.0.2:9100'
- '10.10.0.3:9100'
The local job needs one extra line, because Prometheus and node_exporter do not share a network namespace here — only node_exporter runs with network_mode: host. That's what the extra_hosts: ["host.docker.internal:host-gateway"] line in the compose file above is for: it gives the Prometheus container an alias that resolves to the VPS itself, which is why the job here targets host.docker.internal:9100 instead of localhost:9100. The remote job is the interesting decision: those 10.10.0.x addresses are not public IPs, they are addresses on a private network the other VPSes join.Der lokale Job braucht eine zusätzliche Zeile, weil Prometheus und node_exporter sich hier keinen Netzwerk-Namespace teilen — nur node_exporter läuft mit network_mode: host. Dafür ist die Zeile extra_hosts: ["host.docker.internal:host-gateway"] in der Compose-Datei oben da: Sie gibt dem Prometheus-Container einen Alias, der sich auf den VPS selbst auflöst, weshalb der Job hier host.docker.internal:9100 statt localhost:9100 als Ziel hat. Der Remote-Job ist die interessante Entscheidung: Diese 10.10.0.x-Adressen sind keine öffentlichen IPs, sondern Adressen in einem privaten Netzwerk, dem die anderen VPS beitreten.
node_exporter has no authentication and no TLS built in — anyone who reaches port 9100 gets the full metrics text, no login required. Fine on localhost or inside a private network you control; a bad idea on the open internet, since it hands out hostnames, mount points, interface names and load figures to anyone who finds the port. So when you monitor a second, third or fourth VPS from the same Prometheus, node_exporter on each box must be reachable only over a private network, never on the public interface. Two ways to get there:node_exporter hat weder Authentifizierung noch TLS eingebaut — wer auch immer Port 9100 erreicht, bekommt den vollständigen Metrik-Text, kein Login nötig. In Ordnung auf localhost oder innerhalb eines privaten Netzwerks, das du kontrollierst; eine schlechte Idee im offenen Internet, weil es Hostnamen, Mountpoints, Interface-Namen und Lastwerte an jeden herausgibt, der den Port findet. Überwachst du also einen zweiten, dritten oder vierten VPS vom selben Prometheus aus, muss node_exporter auf jeder Maschine ausschließlich über ein privates Netzwerk erreichbar sein, nie über das öffentliche Interface. Zwei Wege dorthin:
10.10.0.2, and node_exporter binds to that address instead of 0.0.0.0; Prometheus scrapes the WireGuard IP and 9100 is never exposed publicly. WireGuard VPN on a VPS covers setting this up.WireGuard zwischen den Maschinen. Jeder VPS tritt einem kleinen Mesh bei, bekommt eine private Adresse wie 10.10.0.2, und node_exporter bindet an diese Adresse statt an 0.0.0.0; Prometheus scrapt die WireGuard-IP, und 9100 wird nie öffentlich exponiert. WireGuard-VPN auf einem VPS behandelt die Einrichtung.--web.config.file that adds TLS and HTTP basic auth to its own listener — check the exporter's own documentation for the current config format before relying on it.TLS und Basic Auth vor node_exporter, falls ein privates Netzwerk nicht praktikabel ist. Es unterstützt ein --web.config.file, das TLS und HTTP-Basic-Auth zum eigenen Listener hinzufügt — prüfe die Dokumentation des Exporters selbst auf das aktuelle Konfigurationsformat, bevor du dich darauf verlässt.That gives this stack an unusually clean reachability split: Grafana behind Caddy or nginx on 443 is the entire public surface; node_exporter stays private as above, on this box and every other one you monitor; and Prometheus never needs to be reachable from outside at all, since Grafana reaches it over the compose network at http://prometheus:9090 — the loopback-bound published port exists only for a reverse proxy running directly on this host, not for Grafana's own connection. Of three services here, exactly one needs a public port. If your plan gives you NAT IPv4 — a shared address with a small number of forwarded ports rather than 80 and 443 of your own — that is genuinely fine, because Grafana on 443 is the only thing competing for a forwarded port. A dedicated IPv4 only starts to matter if other public services on the same box also want 443 — on our plans that is available on request by e-mail, not as a self-service add-on. Read NAT IPv4 vs a dedicated IP and NAT IPv4, ports and forwarding if you have not sorted out which ports your plan forwards.Das ergibt für diesen Stack eine ungewöhnlich saubere Aufteilung der Erreichbarkeit: Grafana hinter Caddy oder nginx auf 443 ist die gesamte öffentliche Angriffsfläche; node_exporter bleibt privat wie oben beschrieben, auf dieser Maschine und jeder anderen, die du überwachst; und Prometheus muss überhaupt nie von außen erreichbar sein, da Grafana es über das Compose-Netzwerk unter http://prometheus:9090 erreicht — der an Loopback gebundene veröffentlichte Port existiert nur für einen Reverse-Proxy, der direkt auf diesem Host läuft, nicht für Grafanas eigene Verbindung. Von drei Services hier braucht genau einer einen öffentlichen Port. Gibt dir dein Tarif NAT-IPv4 — eine geteilte Adresse mit einer kleinen Anzahl weitergeleiteter Ports statt eigenen 80 und 443 — ist das wirklich in Ordnung, weil Grafana auf 443 das Einzige ist, das um einen weitergeleiteten Port konkurriert. Eine dedizierte IPv4 wird erst relevant, wenn andere öffentliche Dienste auf derselben Maschine ebenfalls 443 wollen — bei unseren Tarifen ist das auf Anfrage per E-Mail verfügbar, nicht als Self-Service-Add-on. Lies NAT IPv4 vs. dedizierte IP und NAT IPv4, Ports und Weiterleitung, falls du noch nicht geklärt hast, welche Ports dein Tarif weiterleitet.
Grafana's first login is admin / admin, and it forces a password change immediately on sign-in — do it right away rather than leaving the default sitting behind whatever reverse proxy you put up next.Grafanas erster Login ist admin / admin, und es erzwingt sofort nach der Anmeldung eine Passwortänderung — erledige das gleich, statt den Standard hinter welchem Reverse-Proxy auch immer du als Nächstes aufsetzt, stehen zu lassen.
Add Prometheus as a data source under Connections → Data sources → Add data source → Prometheus, and set the URL to http://prometheus:9090 — the compose service name, not localhost, since Grafana reaches across the Docker network to a different container. Save and test; a green check confirms it can actually query.Füge Prometheus als Datenquelle unter Connections → Data sources → Add data source → Prometheus hinzu und setze die URL auf http://prometheus:9090 — der Compose-Servicename, nicht localhost, da Grafana über das Docker-Netzwerk einen anderen Container erreicht. Speichern und testen; ein grüner Haken bestätigt, dass tatsächlich abgefragt werden kann.
From there, import the Node Exporter Full community dashboard, built around node_exporter's own metric names with no editing needed. Go to Dashboards → New → Import and paste in its dashboard ID — search "Node Exporter Full" on grafana.com's dashboard library and use the ID shown on that page rather than trusting a number typed from memory, since community dashboard IDs occasionally get retired or replaced and the library page is the source of truth for which one is current. Point it at the Prometheus data source you added and it should populate within a couple of scrape intervals.Importiere von dort das Community-Dashboard Node Exporter Full, das direkt um die eigenen Metriknamen von node_exporter herum gebaut ist, ohne dass etwas bearbeitet werden muss. Geh zu Dashboards → New → Import und füge dessen Dashboard-ID ein — suche „Node Exporter Full“ in grafana.coms Dashboard-Bibliothek und verwende die dort angezeigte ID, statt einer aus dem Gedächtnis getippten Zahl zu vertrauen, da Community-Dashboard-IDs gelegentlich zurückgezogen oder ersetzt werden und die Bibliotheksseite die maßgebliche Quelle dafür ist, welche gerade aktuell ist. Zeig es auf die hinzugefügte Prometheus-Datenquelle, und es sollte sich innerhalb weniger Scrape-Intervalle füllen.
--storage.tsdb.retention.time=15d tells Prometheus to delete data older than 15 days. Disk use tracks two things: how many time series you store (roughly targets times metrics per target times label combinations) and how long you keep them. A handful of hosts on node_exporter's default collectors, scraped every 15 seconds, is genuinely small — comfortably under a few hundred MB at 15 days. That climbs fast in two ways: many more scrape targets, and unbounded label cardinality (a label with a unique value per request multiplies the series stored — the exporters here don't do this, but a badly configured application exporter can). Fifteen days is a reasonable middle ground; extend it once disk headroom is confirmed, and check du -sh on the Prometheus volume occasionally rather than assuming the flag alone caps growth.--storage.tsdb.retention.time=15d weist Prometheus an, Daten zu löschen, die älter als 15 Tage sind. Der Speicherverbrauch hängt von zwei Dingen ab: wie viele Zeitreihen du speicherst (grob Targets mal Metriken pro Target mal Label-Kombinationen) und wie lange du sie behältst. Eine Handvoll Hosts mit node_exporters Standard-Collectors, alle 15 Sekunden gescrapt, ist wirklich klein — bequem unter ein paar hundert MB bei 15 Tagen Retention. Das steigt auf zwei Arten schnell an: deutlich mehr Scrape-Targets, und unbegrenzte Label-Kardinalität (ein Label mit einem eindeutigen Wert pro Request vervielfacht die gespeicherten Zeitreihen — die Exporter hier tun das nicht, aber ein schlecht konfigurierter Anwendungs-Exporter kann es). Fünfzehn Tage sind ein vernünftiger Mittelweg; verlängere die Retention, sobald genug Speicherplatz-Spielraum bestätigt ist, und prüfe gelegentlich du -sh auf dem Prometheus-Volume, statt anzunehmen, dass allein das Flag das Wachstum deckelt.
Grafana can alert on its own, without a separate Alertmanager container — the simpler path for a handful of hosts. Alert rules live under Alerting → Alert rules, built against a Prometheus query with a threshold and an evaluation interval; where an alert goes is configured separately under Alerting → Contact points — email, Slack, a generic webhook, Telegram and others out of the box, each with a Test button worth pressing before you trust it. Notification policies route firing alerts to contact points by label, so a disk-space alert on one host and a CPU alert on another can page different places without duplicating the rule.Grafana kann selbst alarmieren, ohne einen separaten Alertmanager-Container — der einfachere Weg für eine Handvoll Hosts. Alert-Regeln liegen unter Alerting → Alert rules, aufgebaut auf einer Prometheus-Query mit Schwellwert und Auswertungsintervall; wohin ein Alert geht, wird separat unter Alerting → Contact points konfiguriert — E-Mail, Slack, ein generischer Webhook, Telegram und andere von Haus aus, jeweils mit einem Test-Button, den es sich lohnt zu drücken, bevor du ihm vertraust. Notification-Policies leiten auslösende Alerts anhand von Labels an Kontaktpunkte weiter, sodass ein Disk-Space-Alert auf einem Host und ein CPU-Alert auf einem anderen unterschiedliche Stellen benachrichtigen können, ohne die Regel zu duplizieren.
Everything that matters lives in the two named volumes: grafana-data holds every dashboard, data source and user account set up by hand, and prometheus-data holds the metric history nothing else has a copy of. Stop the container before copying either, since both write continuously while running:Alles, was zählt, lebt in den beiden benannten Volumes: grafana-data enthält jedes von Hand eingerichtete Dashboard, jede Datenquelle und jeden Benutzeraccount, und prometheus-data enthält die Metrik-Historie, von der es sonst keine Kopie gibt. Stoppe den Container, bevor du eines von beiden kopierst, da beide kontinuierlich schreiben, während sie laufen:
docker compose stop grafana
docker cp $(docker compose ps -aq grafana):/var/lib/grafana ./grafana-backup-$(date +%F)
docker compose start grafana
docker compose stop prometheus
docker cp $(docker compose ps -aq prometheus):/prometheus ./prometheus-backup-$(date +%F)
docker compose start prometheus
Copy both directories off the VPS entirely — a copy on the same disk as the volumes it backs up disappears with that disk. back up your VPS covers what off-machine backup looks like in practice. Losing prometheus-data costs you history, not configuration — the scrape config lives in prometheus.yml on disk, not inside the volume. Losing grafana-data is more annoying, since every dashboard edit and contact point configured by hand through the UI goes with it.Kopiere beide Verzeichnisse vollständig vom VPS herunter — eine Kopie auf derselben Platte wie die Volumes, die sie sichert, verschwindet mit dieser Platte. Backup deines VPS beschreibt, wie Backup abseits der Maschine in der Praxis aussieht. prometheus-data zu verlieren kostet dich Historie, nicht Konfiguration — die Scrape-Konfiguration liegt in prometheus.yml auf der Platte, nicht im Volume. grafana-data zu verlieren ist ärgerlicher, da jede Dashboard-Bearbeitung und jeder von Hand über die UI konfigurierte Kontaktpunkt damit verloren geht.
Updates are the usual two lines:Updates sind die üblichen zwei Zeilen:
docker compose pull
docker compose up -d
Bump each pinned tag deliberately rather than tracking latest — check the current release on each project's own tag list first. Prometheus and Grafana both handle their own on-disk migrations on startup, and taking the backups above immediately before a version jump buys you a way back if a new schema disagrees with what you already have.Erhöhe jeden gepinnten Tag bewusst, statt latest zu verfolgen — prüfe zuerst die aktuelle Version in der eigenen Tag-Liste jedes Projekts. Prometheus und Grafana handhaben beim Start beide ihre eigenen On-Disk-Migrationen, und die obigen Backups unmittelbar vor einem Versionssprung zu machen, verschafft dir einen Weg zurück, falls ein neues Schema nicht zu dem passt, was du bereits hast.
This whole stack — node_exporter, Prometheus and Grafana together — idles comfortably inside a 2 GiB Basic. Grafana alone sits at a few hundred MiB even doing nothing, since it is a full Go web server with its own SQLite database for dashboards and users; Prometheus adds more depending on targets and retention, but a handful of hosts at 15 days is small next to 2 GiB of RAM. What pushes you to move up a tier is not this stack alone — it is dozens of targets instead of a handful, months of retention instead of weeks, or other containers doing real work on the same box.Dieser gesamte Stack — node_exporter, Prometheus und Grafana zusammen — läuft im Leerlauf bequem in einem 2-GiB-Basic. Grafana allein liegt schon im Ruhezustand bei ein paar hundert MiB, weil es ein vollständiger Go-Webserver mit eigener SQLite-Datenbank für Dashboards und Benutzer ist; Prometheus kommt je nach Targets und Retention noch dazu, aber eine Handvoll Hosts bei 15 Tagen ist klein neben 2 GiB RAM. Was dich dazu bringt, eine Stufe höher zu gehen, ist nicht dieser Stack allein — es sind Dutzende Targets statt einer Handvoll, Monate statt Wochen an Retention, oder andere Container, die echte Arbeit auf derselben Maschine erledigen.
Full disclosure: this is what we sell. A 2 GiB Basic is comfortable for Grafana and Prometheus watching a handful of hosts at a 15-day retention; move up a tier once you are scraping many more targets or keeping months of history instead of weeks.Zur vollen Transparenz: Das ist, was wir verkaufen. Ein 2-GiB-Basic ist komfortabel für Grafana und Prometheus, die eine Handvoll Hosts bei 15 Tagen Retention überwachen; steige eine Stufe höher, sobald du deutlich mehr Targets scrapst oder Monate statt Wochen an Historie vorhältst.
Linux KVM VPS — EUR 4.99 to EUR 59.99 a month, on our own single-tenant bare metal in Dallas, TX and Charlotte, NC. Full hardware virtualisation (KVM), your own kernel, full root. Six tiers, vps-starter to vps-ultra. Starter is 1 vCPU, 1 GiB RAM, 25 GB disk.Linux-KVM-VPS — 4,99 bis 59,99 EUR im Monat, auf unserer eigenen Single-Tenant-Bare-Metal-Hardware in Dallas, TX und Charlotte, NC. Vollständige Hardware-Virtualisierung (KVM), eigener Kernel, volles Root. Sechs Tarife, vps-starter bis vps-ultra. Starter hat 1 vCPU, 1 GiB RAM, 25 GB Speicher.
You order in the shop, pay by card (Stripe) or SEPA bank transfer, and your login details are e-mailed to you once the service is set up. Support is e-mail, run by one person, with no guaranteed response time. All prices are final totals under the German small-business rule (§19 UStG); no VAT is added or shown.Du bestellst im Shop, zahlst per Karte (Stripe) oder SEPA-Überweisung, und deine Zugangsdaten werden dir per E-Mail zugeschickt, sobald der Dienst eingerichtet ist. Support läuft per E-Mail, von einer einzelnen Person betrieben, ohne garantierte Reaktionszeit. Alle Preise sind Endpreise. Gemäß § 19 UStG wird keine Umsatzsteuer ausgewiesen.
Order vps-basic → · Linux KVM VPS overviewvps-basic bestellen → · Übersicht Linux-KVM-VPS
Written by the person who runs overnight.host: a small, honest hosting company on dedicated bare metal — Linux VPS, game servers, web hosting. Live status at up.overnight.host.Geschrieben von der Person, die overnight.host betreibt: ein kleines, ehrliches Hosting-Unternehmen auf dedizierter Bare-Metal-Hardware — Linux-VPS, Gameserver, Webhosting. Live-Status unter up.overnight.host.
Not for a small setup. Grafana's built-in alerting handles rules, contact points and notification policies on its own; Alertmanager is really for when several Prometheus instances need alerts deduplicated and routed centrally, and one VPS with one Prometheus is squarely what Grafana alerting alone covers.Für ein kleines Setup nicht. Grafanas eingebautes Alerting handhabt Regeln, Kontaktpunkte und Notification-Policies ganz allein; Alertmanager ist eigentlich für den Fall gedacht, dass mehrere Prometheus-Instanzen Alerts zentral dedupliziert und geroutet brauchen, und ein VPS mit einem Prometheus ist genau das, was Grafana-Alerting allein abdeckt.
Yes, as long as the exporter on that box is reachable over a private network you control, such as a WireGuard mesh between your VPSes. NAT IPv4 on the target box is irrelevant here — Prometheus initiates the connection over the tunnel, not through a public port forwarded to node_exporter.Ja, solange der Exporter auf dieser Maschine über ein privates Netzwerk erreichbar ist, das du kontrollierst, etwa ein WireGuard-Mesh zwischen deinen VPS. NAT-IPv4 auf der Ziel-Maschine ist hier irrelevant — Prometheus baut die Verbindung über den Tunnel auf, nicht über einen öffentlichen, an node_exporter weitergeleiteten Port.
network_mode: host instead of a normal port mapping?Warum braucht node_exporter network_mode: host statt eines normalen Port-Mappings?Because it needs to read the actual host's /proc, /sys and mount points, and see its real network interfaces, not a container's isolated view. A normal bridge-network container would report container-internal numbers that don't match what is happening on the VPS.Weil es die echten /proc, /sys und Mountpoints des Hosts lesen muss und dessen echte Netzwerk-Interfaces sehen muss, nicht die isolierte Sicht eines Containers. Ein normaler Container im Bridge-Netzwerk würde container-interne Zahlen melden, die nicht dem entsprechen, was tatsächlich auf dem VPS passiert.
Prometheus's default retention is already time-based, but pinning --storage.tsdb.retention.time means you know the number instead of inheriting whatever the image defaults to, which can change between versions. Set it deliberately and check disk usage periodically rather than trusting the assumption either way.Prometheus' Standard-Retention ist bereits zeitbasiert, aber --storage.tsdb.retention.time festzunageln bedeutet, dass du die Zahl kennst, statt zu übernehmen, was auch immer das Image standardmäßig vorgibt, was sich zwischen Versionen ändern kann. Setze sie bewusst und prüfe den Speicherverbrauch regelmäßig, statt dich so oder so auf die Annahme zu verlassen.
It is the obvious first one because it is built directly around node_exporter's own metric names with no query editing needed. Once you are comfortable with Grafana, a smaller dashboard with only the panels you actually look at is often more useful day to day than the full import, which covers far more than most single-host setups need.Es ist das naheliegende erste, weil es direkt um node_exporters eigene Metriknamen herum gebaut ist, ohne dass Queries bearbeitet werden müssen. Sobald du dich mit Grafana wohlfühlst, ist ein kleineres Dashboard mit nur den Panels, die du tatsächlich anschaust, im Alltag oft nützlicher als der vollständige Import, der weit mehr abdeckt, als die meisten Single-Host-Setups brauchen.
Prices are final totals; no VAT is shown (§19 UStG). Need something the shop does not list? Email us for a written offer.Alle Preise sind Endpreise ohne ausgewiesene USt. (§19 UStG). Du brauchst etwas, das nicht im Shop steht? Schreib uns für ein schriftliches Angebot.
Order now →Jetzt bestellen → Request a custom configIndividuelle Konfiguration anfragen