Graaf is a Go program that translates simple dashboards written in YAML to Grafana JSON. It has an extensive manual page that explains its usage. This is also known as Grafana as code, but then for real.

The following is a dashboard with 4 panels that I use for my own monitoring.

title: Overview i
uid: dashboard-overview
panels:
  - timeseries:
      title: 'Network Bytes [5m] - edgemax.i'
      y:
        unit: Bps
      exprs:
        - expr: 'rate(ifHCOutOctets{ifAlias="eth0",job="edgemax"}[5m])'
          legend: '{{ifAlias}} transmit'
        - expr: 'rate(ifHCInOctets{ifAlias="eth0",job="edgemax"}[5m])'
          legend: '{{ifAlias}} receive'
          transform: negative-y
  - timeseries:
      title: 'HTTPS Queries [5m]'
      y:
        unit: rps
      exprs:
        - expr: 'sum by (host) (rate(caddy_http_request_count_total{job="caddy"}[5m]))'
          legend: '{{host}}'
        - expr: 'sum (rate(caddy_http_request_count_total{job="caddy"}[5m]))'
          legend: total
          transform: negative-y
  - timeseries:
      title: 'DNS Queries [5m]'
      y:
        unit: rps
      exprs:
        - expr: 'sum by (zone) (rate(coredns_dns_requests_total{job="nsdns",zone!="dropped"}[5m]))'
          legend: '{{zone}}'
        - expr: 'sum (rate(coredns_dns_requests_total{job="nsdns"}[5m]))'
          legend: total
          transform: negative-y
        - expr: 'sum (rate(coredns_dns_requests_total{job="nsdns",zone="dropped"}[5m]))'
          legend: dropped
          transform: negative-y
  - timeseries:
      title: 'Network Bytes [5m] - nuc.i'
      y:
        unit: Bps
      exprs:
        - expr: 'rate(node_network_transmit_bytes_total{job="node",device="eno1"}[5m])'
          legend: '{{device}} transmit'
          threshold: 'ok{value="0"}'
        - expr: 'rate(node_network_receive_bytes_total{job="node",device="eno1"}[5m])'
          legend: '{{device}} receive'
          transform: negative-y

This dashboard is 47 lines, the generated JSON dashboard is 500 lines, a ~10-fold reduction in cruft. Not bad!

Configure Grafana

In the graaf(1) manual page it is explained how to configure Grafana, so that graaf(1) will actually work. Repeated here for easier search indexing.

For Grafana to work you only may use OrgID 1 and provision the datasource and dashboards via the following YAML config:

In .../provisioning/dashboards/dashboards.yaml, put:

apiVersion: 1

providers:
 - name: 'default'
   orgId: 1
   folder: ''
   folderUid: ''
   type: file
   options:
     path: /var/lib/grafana/dashboards

And save the output of graaf in /var/lig/grafana/dashboards. Next configure a datasource in ../provisioning/datasources/prometheus.yaml:

apiVersion: 1

datasources:
- name: Prometheus
  type: prometheus
  access: proxy
  orgId: 1
  url: http://localhost:9090
  isDefault: true

For good measure erase the grafana database: rm /var/lib/grafana/grafana.db and restart grafana. If you want to set a default ‘home’ dashboard use:

[dashboards]
default_home_dashboard_path = /var/lib/grafana/dashboards/mydashboard.json

in /etc/grafana/grafana.ini.