Infrastructure · Security · Engineering Tell us what you need

Home / Guides / Zabbix, Grafana and Prometheus

Guide

Zabbix, Grafana
and Prometheus.

Three tools that are frequently installed and rarely finished. This covers what each is actually for, how to get one running, and then the part that decides whether monitoring is useful — which checks matter, where thresholds come from, and how to stop alerts nobody reads.

Version
1.0
Last reviewed
August 2026
Covers
Zabbix 7.4 · Grafana 13 · Prometheus 3
Links
Verified at review date
YOUR ESTATE servers network devices databases containers applications COLLECT Zabbix agent SNMP · IPMI exporters agentless checks :10050 · :9100 ZABBIX store · trigger · alert infrastructure-shaped PROMETHEUS scrape · store · rule application-shaped GRAFANA one view across both dashboards exploration reads, does not collect A PERSON who is asked to do something about it THE TOOLS ARE NOT ALTERNATIVES. THEY DO DIFFERENT PARTS OF THE SAME JOB.

Grafana reads. It does not collect, store or alert on its own.

Before you run anything

This guide is general information about how we approach monitoring. It is not advice for your specific environment. Commands are shown for reference; read each one before running it, and install packages on a system where you are permitted to do so.

Version numbers, package names and download URLs were checked at the review date shown above and move quickly. Follow the official download pages linked at the end rather than copying a version number from here. MDCS accepts no liability for decisions taken on the basis of this guide.

Which tool, for what

The three are routinely presented as competitors. They are not, and choosing between them as though they were is the first mistake.

ToolWhat it isReach for it when
ZabbixA complete monitoring system: collection, storage, triggers, alerting and a web interface. Templates for common devices, agent and agentless checks, and native SNMP.The estate is infrastructure — servers, switches, hypervisors, storage, UPS. Anything that will never expose a metrics endpoint.
PrometheusA time-series database that pulls metrics from HTTP endpoints, with a query language and its own alerting component.The estate is applications and containers, and the things being monitored can be made to expose metrics themselves.
GrafanaA visualisation layer. It queries other systems and draws them. It stores no metrics of its own.You have more than one source and want one place to look at them, or you want dashboards better than the built-in ones.

The common arrangement

Zabbix for the infrastructure, Prometheus for anything containerised, and Grafana over both so there is one place to look. Zabbix has its own perfectly usable interface, so Grafana is optional — add it when you have a second data source or a dashboard requirement Zabbix cannot meet.

Do not run all three because a guide listed all three

A single Zabbix server monitoring a hundred hosts is a complete, working monitoring system. Adding Prometheus and Grafana to that adds two more things to maintain, upgrade and secure. Add each one when there is a question the existing setup cannot answer.

01 · Zabbix — getting started

Zabbix ships a repository per version and per distribution. Install the repository package first, then the components, then set up the database. Version 7.0 is the current long-term support release; 7.4 is the current standard release.

Use the official repository, not your distribution's

Debian and Ubuntu ship their own Zabbix packages. They lag behind and Zabbix does not support them. The download page generates the exact commands for your combination of version, distribution, database and web server — that is the reliable route, and it is why this guide does not reproduce a fixed URL.

Server, frontend and database — the shape of it

# 1. install the repository package generated for you at zabbix.com/download # then: sudo apt update sudo apt install zabbix-server-mysql zabbix-frontend-php \ zabbix-apache-conf zabbix-sql-scripts zabbix-agent # 2. create the database and user sudo mysql -uroot -p CREATE DATABASE zabbix CHARACTER SET utf8mb4 COLLATE utf8mb4_bin; CREATE USER zabbix@localhost IDENTIFIED BY 'choose-a-real-password'; GRANT ALL PRIVILEGES ON zabbix.* TO zabbix@localhost; SET GLOBAL log_bin_trust_function_creators = 1; QUIT; # 3. import the schema (takes a few minutes) sudo zcat /usr/share/zabbix/sql-scripts/mysql/server.sql.gz | mysql -uzabbix -p zabbix # 4. turn the setting back off, set DBPassword, start sudo mysql -uroot -p -e "SET GLOBAL log_bin_trust_function_creators = 0;" sudo sed -i 's/^# DBPassword=/DBPassword=/' /etc/zabbix/zabbix_server.conf sudo systemctl restart zabbix-server zabbix-agent apache2 sudo systemctl enable zabbix-server zabbix-agent apache2

The frontend then answers on /zabbix on that host. The default account is Admin with password zabbix — change it before the machine is reachable by anyone else.

Package names differ by database and web server. The commands above assume MySQL or MariaDB with Apache; PostgreSQL and nginx use different package names, which the download page will give you.

Agents on the machines you want to watch

# on each monitored host, after installing the repository package sudo apt install zabbix-agent2 # point it at the server sudo sed -i 's/^Server=127.0.0.1/Server=YOUR.ZABBIX.SERVER.IP/' /etc/zabbix/zabbix_agent2.conf sudo sed -i 's/^ServerActive=127.0.0.1/ServerActive=YOUR.ZABBIX.SERVER.IP/' /etc/zabbix/zabbix_agent2.conf sudo sed -i 's/^Hostname=Zabbix server/Hostname=this-host-name/' /etc/zabbix/zabbix_agent2.conf sudo systemctl restart zabbix-agent2 && sudo systemctl enable zabbix-agent2 # from the Zabbix server, confirm it answers zabbix_get -s MONITORED.HOST.IP -k agent.ping

A reply of 1 means the agent is reachable and the firewall allows it. The agent listens on 10050; the server listens on 10051. Both need to be open in the right direction.

Hostname must match exactly what you enter when adding the host in the frontend. A mismatch is the single most common reason a newly added host stays grey and never collects.

02 · Zabbix — making it useful

A default install with a linked template collects a great deal and tells you very little. These are the changes worth making first.

Recipe: stop the disk-space alert that fires every week

The stock filesystem trigger fires at a fixed percentage. On a 4 TB volume, 20% free is 800 GB and nothing is wrong. On a 40 GB root volume, 20% free is a genuine warning.

# In the template: Data collection > Templates > Linux by Zabbix agent # Find the macro and override it per host or per host group. {$VFS.FS.PUSED.MAX.WARN} # default 80 — set 90 on large data volumes {$VFS.FS.PUSED.MAX.CRIT} # default 90 — set 95 on large data volumes # Better on large filesystems: alert on absolute free space instead, # by cloning the trigger and using the bytes item rather than the percentage.

Macros can be set at template, host group or host level. Set them on the host group so a new server of the same kind inherits the right threshold rather than the default.

Recipe: alert on trend, not on a single sample

A trigger that fires the moment CPU touches 90% will fire during every backup. One that fires when CPU has averaged above 90% for fifteen minutes describes a problem.

# trigger expression — sustained, not instantaneous avg(/HOST/system.cpu.util,15m) > 90 # memory available for the last 10 minutes, in bytes avg(/HOST/vm.memory.size[available],10m) < 1G # disk filling: projected to be full within 24 hours timeleft(/HOST/vfs.fs.size[/,pused],1h,100) < 24h # service stopped, confirmed across two checks rather than one last(/HOST/proc.num[nginx],#1)=0 and last(/HOST/proc.num[nginx],#2)=0

The third is the most useful trigger most estates are missing. It predicts a full disk a day ahead rather than reporting one at 3am.

Recipe: watch a service that has no template

# on the agent host, add a user parameter echo 'UserParameter=queue.depth,/usr/local/bin/queue-depth.sh' \ | sudo tee /etc/zabbix/zabbix_agent2.d/custom.conf sudo systemctl restart zabbix-agent2 # from the server, confirm it returns a number zabbix_get -s MONITORED.HOST.IP -k queue.depth

The script runs as the zabbix user. If it needs privileges, grant them narrowly through sudoers for that one command rather than running the agent as root.

Two settings worth changing on day one

Housekeeping. Default history retention keeps a great deal of raw data. On a busy server this becomes the reason the database grows and the frontend slows. Set history to something short — a week or two — and let trends carry the long view.

Action escalation. Configure escalation steps so an unacknowledged problem repeats and then reaches somebody else. Without it, a missed notification is simply missed.

03 · Prometheus — getting started

Prometheus is a single binary with a configuration file. It pulls metrics from endpoints you list; it does not receive pushes, and nothing is installed on the targets except an exporter that publishes a metrics page.

Prometheus itself

# take the current release from prometheus.io/download # then, substituting the version you downloaded: tar xvf prometheus-*.linux-amd64.tar.gz cd prometheus-*.linux-amd64 sudo cp prometheus promtool /usr/local/bin/ sudo mkdir -p /etc/prometheus /var/lib/prometheus sudo cp -r consoles console_libraries prometheus.yml /etc/prometheus/ # check the config before starting anything promtool check config /etc/prometheus/prometheus.yml

node_exporter, on each machine you want to watch

# from github.com/prometheus/node_exporter/releases tar xvf node_exporter-*.linux-amd64.tar.gz sudo cp node_exporter-*/node_exporter /usr/local/bin/ sudo useradd -rs /bin/false node_exporter # confirm it is publishing before wiring anything to it node_exporter & curl -s localhost:9100/metrics | head -20

node_exporter publishes on 9100. Every metric it produces is prefixed node_, which makes them easy to find in a query.

Run it as a systemd service under the node_exporter user rather than from a shell, and bind it to an address Prometheus can reach but the internet cannot.

Telling Prometheus what to scrape

# /etc/prometheus/prometheus.yml global: scrape_interval: 15s evaluation_interval: 15s rule_files: - "rules/*.yml" scrape_configs: - job_name: 'prometheus' static_configs: - targets: ['localhost:9090'] - job_name: 'nodes' static_configs: - targets: ['web01:9100', 'web02:9100', 'db01:9100'] labels: env: production

Labels applied here follow the metric everywhere. Adding env and role at scrape time is what later allows one alert rule to cover every production web server.

04 · Prometheus — useful queries and rules

PromQL is the reason to use Prometheus and the reason people give up on it. These cover most of what an infrastructure estate needs.

Recipe: the queries worth saving

# CPU used, as a percentage, per instance 100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) # memory used, as a percentage 100 * (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) # root filesystem used, as a percentage 100 - (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"} * 100) # disk full prediction: will it fill within four hours? predict_linear(node_filesystem_avail_bytes{mountpoint="/"}[6h], 4*3600) < 0 # which targets are down right now up == 0 # request error rate over five minutes, for an instrumented application sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m]))

rate() before avg(), not the other way round. Averaging a counter and then taking the rate produces a number that looks plausible and is wrong.

Recipe: alert rules that survive contact with reality

# /etc/prometheus/rules/node.yml groups: - name: node rules: - alert: InstanceDown expr: up == 0 for: 5m labels: {severity: critical} annotations: summary: "{{ $labels.instance }} has not responded for 5 minutes" - alert: DiskWillFill expr: predict_linear(node_filesystem_avail_bytes{mountpoint="/"}[6h], 4*3600) < 0 for: 30m labels: {severity: warning} annotations: summary: "{{ $labels.instance }} root filesystem projected full within 4 hours" - alert: HighMemory expr: 100 * (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) > 90 for: 15m labels: {severity: warning}

The for clause is the difference between a monitoring system and a pager that goes off during backups. Nothing should alert without one.

# always validate rules before reloading promtool check rules /etc/prometheus/rules/node.yml curl -X POST http://localhost:9090/-/reload

The reload endpoint only works if Prometheus was started with --web.enable-lifecycle. Without that flag it returns 404, which is easily mistaken for a broken URL.

05 · Grafana — getting started

Grafana installs from its own APT or YUM repository. It answers on port 3000, and the first login is admin / admin, which it will ask you to change.

# Debian and Ubuntu — see grafana.com/grafana/download for the current lines sudo apt install -y apt-transport-https software-properties-common wget sudo mkdir -p /etc/apt/keyrings/ wget -q -O - https://apt.grafana.com/gpg.key \ | gpg --dearmor | sudo tee /etc/apt/keyrings/grafana.gpg > /dev/null echo "deb [signed-by=/etc/apt/keyrings/grafana.gpg] https://apt.grafana.com stable main" \ | sudo tee /etc/apt/sources.list.d/grafana.list sudo apt update && sudo apt install grafana sudo systemctl enable --now grafana-server

Then add a data source: Prometheus at http://localhost:9090, or Zabbix through the Zabbix plugin, which needs the Zabbix API URL and a Zabbix account with read permission.

Grafana is a web application with credentials and access to your monitoring data. Put it behind TLS and do not publish it to the internet without authentication in front of it.

06 · Grafana — dashboards people actually open

Recipe: import before you build

The dashboard library has thousands of community dashboards. Importing one is a numeric ID, and it is a far better starting point than an empty canvas.

# In Grafana: Dashboards > New > Import > enter the ID > select your data source # Browse and search IDs at grafana.com/grafana/dashboards # Filter by data source — Prometheus or Zabbix — before choosing.

Community dashboards are written against particular exporter versions and metric names. If panels show "No data", the usual cause is a metric name that has changed. Edit the panel and check the query against what your exporter actually publishes.

Recipe: three dashboards, not one

DashboardAnswersKeep it to
OperationsIs anything broken right now?One screen. Red or green. No history, no trends, nothing that needs interpretation.
CapacityWhat will run out, and when?Trends over weeks and months. Disk growth, memory headroom, connection counts.
ServiceIs the thing users care about working?Response time, error rate, throughput. No CPU graphs — nobody outside the team cares about CPU.

The test for a panel

If somebody looked at it and could not say what they would do differently, it belongs on a different dashboard or on none. A dashboard is not an inventory of available metrics.

Recipe: variables, so one dashboard serves the estate

# Dashboard settings > Variables > New variable > Query # Prometheus — a dropdown of every instance being scraped: label_values(node_uname_info, instance) # then use it in every panel query: 100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle",instance="$instance"}[5m])) * 100)

One dashboard with a host dropdown replaces one dashboard per host, and it stays correct when servers are added.

Thresholds, and where they come from

This is the part that decides whether any of the above is worth having, and it cannot be copied from a guide — including this one.

The number that tells you whether it is working

Alerts per week, and what proportion required an action. If the second figure is below about half, the system is training people to dismiss it — and the alert that mattered will be dismissed with the rest.

Checked at the review date at the top of this page. At that date Zabbix was at 7.4 with 7.0 as the long-term support branch — take LTS if you would rather upgrade less often. Grafana was at 13, Prometheus at 3. Versions and package names change; follow the official pages rather than copying a version number from any article, including this one.

Zabbix

Prometheus

Grafana

Where to start if you take one link

The Grafana dashboard library. Importing a well-made dashboard for something you already collect teaches more about what good monitoring looks like than any amount of building from scratch.

If you would rather not

Or we can build and tune it.

Everything above is what we do at the start of a monitoring engagement. The part that takes longest is the tuning — weeks of watching, then removing everything that fired without needing action. If you have the tools installed and nobody trusts the alerts, that is the engagement worth having.

Monitoring watches for failure. Watching for attack is a different service — see SOC and SIEM monitoring.