Monitoring

Two Monitoring Stacks from Scratch: Zabbix + Vue Dashboard and Prometheus + Grafana

30.06.2026  · 

Why two stacks

Monitoring has two industry-standard approaches: Zabbix (classic, push-oriented) and Prometheus + Grafana (cloud-native, pull model). I deployed both for the same infrastructure — to understand the difference in practice, not from articles. The monitored fleet is real: a cloud server in Moscow, a home Proxmox VE hypervisor, and several virtual machines.

Stack one: Zabbix + a custom Vue/Nuxt dashboard

I set up Zabbix 7.0 LTS with PostgreSQL in a dedicated LXC container on Proxmox. Agents collect full metrics from every node — CPU, memory, disks, network. The stock Zabbix web UI didn’t satisfy me as a showcase, so I wrote my own dashboard in Vue 3 / Nuxt 3 — no third-party UI libraries, with live charts in pure SVG. The key decision is a BFF (Backend-for-Frontend) architecture: the browser talks only to my Nuxt server, while the Zabbix access token lives strictly in the server environment and never leaves it. Zabbix itself is not exposed to the internet.

Live demo: johngorn.store

Stack two: Prometheus + node_exporter + Grafana

The second container runs Prometheus, which scrapes node_exporter on each node, with Grafana on top. A fundamentally different model: pull instead of push, queries in PromQL, all configuration as text (infrastructure as code). Grafana dashboards are set up via provisioning — declaratively, in files, not by clicking.

Live demo: grafana.johngorn.store

Linking cloud and home with WireGuard

The cloud server and the home lab are connected over WireGuard — a mesh of five nodes. The cloud server is monitored through this encrypted tunnel: agents and exporters listen only on the VPN interface and are unreachable from the internet. No monitoring ports exposed publicly.

The interesting part — safe publishing

I wanted to show both dashboards openly. But a public monitoring system risks leaking the infrastructure map: internal IPs, server names, topology. I solved this in layers.

For the Zabbix dashboard I built two modes: public (node roles plus metrics only) and private behind a login (real data). The key point is filtering at the API level, not cosmetics in the UI: an unauthenticated request physically receives no internal addresses — they aren’t even in the raw response.

For Grafana the task was trickier. Anonymous access is limited to viewing a single dashboard, with no Explore section. Internal addresses are masked into roles at the metric-collection stage (relabel in Prometheus — the IP never enters storage at all). Then I found a non-obvious leak channel: through Grafana’s datasource proxy an anonymous user could reach Prometheus service endpoints where the real addresses are visible. I blocked those at the reverse-proxy level. The lesson: anonymizing monitoring isn’t «hide one IP» — it’s an audit of every channel through which the infrastructure can surface.

The result

Two complete monitoring systems for one fleet, both with live public demos and no leakage of internal data. Along the way — WireGuard, a reverse proxy with Let’s Encrypt, systemd, containerization, and an understanding of how the pull model differs from push not in theory, but on my own servers.

Technologies: Zabbix 7.0, Prometheus, Grafana, node_exporter, Vue 3 / Nuxt 3, WireGuard, nginx, Let’s Encrypt, Proxmox VE, Docker.

← Turnkey Websites: From Design to Self-Hosted Infrastructure Infrastructure as Code: 9 Ansible Roles That Rebuild My Monitoring in Minutes →