about summary refs log tree commit diff
path: root/bundles/prometheus/files/consoles
diff options
context:
space:
mode:
authorRobert Günzler <r@gnzler.io>2026-02-23 14:19:55 +0100
committerRobert Günzler <r@gnzler.io>2026-02-23 14:19:55 +0100
commitc9197b9bfd5cd3ab49ee7dfdc81d207715ccd5d8 (patch)
treece772751fa5fbde24b69f8de95280fe0b01a5b6a /bundles/prometheus/files/consoles
parent98470f7be2244d2c40c0bd547eeef5b3bde2e858 (diff)
prometheus: modernize console + alerts
Signed-off-by: Robert Günzler <r@gnzler.io>
Diffstat (limited to 'bundles/prometheus/files/consoles')
-rw-r--r--bundles/prometheus/files/consoles/cpu.html (renamed from bundles/prometheus/files/consoles/node-cpu.html)0
-rw-r--r--bundles/prometheus/files/consoles/disk.html (renamed from bundles/prometheus/files/consoles/node-disk.html)0
-rw-r--r--bundles/prometheus/files/consoles/index.html163
-rw-r--r--bundles/prometheus/files/consoles/node-overview.html121
-rw-r--r--bundles/prometheus/files/consoles/node.html133
-rw-r--r--bundles/prometheus/files/consoles/prom.html88
-rw-r--r--bundles/prometheus/files/consoles/prometheus-overview.html96
7 files changed, 313 insertions, 288 deletions
diff --git a/bundles/prometheus/files/consoles/node-cpu.html b/bundles/prometheus/files/consoles/cpu.html
index 64a2cad..64a2cad 100644
--- a/bundles/prometheus/files/consoles/node-cpu.html
+++ b/bundles/prometheus/files/consoles/cpu.html
diff --git a/bundles/prometheus/files/consoles/node-disk.html b/bundles/prometheus/files/consoles/disk.html
index 52afac7..52afac7 100644
--- a/bundles/prometheus/files/consoles/node-disk.html
+++ b/bundles/prometheus/files/consoles/disk.html
diff --git a/bundles/prometheus/files/consoles/index.html b/bundles/prometheus/files/consoles/index.html
index 29c2884..f0b102d 100644
--- a/bundles/prometheus/files/consoles/index.html
+++ b/bundles/prometheus/files/consoles/index.html
@@ -1,81 +1,102 @@
-{{/* vim: set ft=html: */}}
+{{/* vim: set ft=gotmpl: */}}
 
-{{ define "menu" }}
-foobar
+{{ define "s6_rc_services_count" }}
+{{ $instance := .arg0 }}{{ $bundle := .arg1 }}
+{{ template "prom_query_drilldown" (args (printf "s6_rc_services_count{instance='%s',bundle='%s',service=~'.*-srv'}" $instance $bundle)) }}
 {{ end }}
 
 {{ template "head" . }}
 
-{{ template "prom_right_table_head" . }}
-<tr>
-	<th colspan="2">S6 - inazuma</th>
-</tr>
-<tr>
-	<td>default</td>
-	<td>{{ template "prom_query_drilldown" (args (printf "s6_rc_services_count{job='node-exporter',instance='inazuma',bundle='default'}")) }}</td>
-</tr>
-<tr>
-	<td>containers</td>
-	<td>{{ template "prom_query_drilldown" (args (printf "s6_rc_services_count{job='node-exporter',instance='inazuma',bundle='containers'}")) }}</td>
-</tr>
-<tr>
-	<td>www</td>
-	<td>{{ template "prom_query_drilldown" (args (printf "s6_rc_services_count{job='node-exporter',instance='inazuma',bundle='www'}")) }}</td>
-</tr>
-<tr>
-	<td>services</td>
-	<td>{{ template "prom_query_drilldown" (args (printf "s6_rc_services_count{job='node-exporter',instance='inazuma',bundle='services'}")) }}</td>
-</tr>
-<tr>
-	<th colspan="2">S6 - shimakaze</th>
-</tr>
-<tr>
-	<td>default</td>
-	<td>{{ template "prom_query_drilldown" (args (printf "s6_rc_services_count{job='node-exporter',instance='shimakaze',bundle='default'}")) }}</td>
-</tr>
-<tr>
-	<td>containers</td>
-	<td>{{ template "prom_query_drilldown" (args (printf "s6_rc_services_count{job='node-exporter',instance='shimakaze',bundle='containers'}")) }}</td>
-</tr>
-<tr>
-	<td>www</td>
-	<td>{{ template "prom_query_drilldown" (args (printf "s6_rc_services_count{job='node-exporter',instance='shimakaze',bundle='www'}")) }}</td>
-</tr>
-<tr>
-	<td>services</td>
-	<td>{{ template "prom_query_drilldown" (args (printf "s6_rc_services_count{job='node-exporter',instance='shimakaze',bundle='services'}")) }}</td>
-</tr>
-{{ template "prom_right_table_tail" . }}
-
 {{ template "prom_content_head" . }}
 <div class="prom_content_div">
-	<h1>Overview</h1>
-	<h3>Traefik Requests OK - inazuma</h3>
-	<div id="traefikGraph"></div>
-	<script>
-		new PromConsole.Graph({
-			node: document.querySelector("#traefikGraph"),
-			expr: "irate(traefik_service_request_duration_seconds_count{job='traefik',instance='inazuma',code='200'}[5m])",
-			name: '[[service]]',
-			yAxisFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
-			yHoverFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
-			yTitle: "Requests",
-			yUnits: "/s",
-		})
-	</script>
-	<h3>Jellyfin HTTP Requests - shimakaze</h3>
-	<div id="jellyfinGraph"></div>
-	<script>
-		new PromConsole.Graph({
-			node: document.querySelector("#jellyfinGraph"),
-			expr: "irate(http_request_duration_seconds_count{job='jellyfin',instance='shimakaze'}[5m])",
-			name: '[[action]]',
-			yAxisFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
-			yHoverFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
-			yTitle: "Requests",
-			yUnits: "/s",
-		})
-	</script>
+  <div>
+    <h2>Overview</h2>
+    <code>IZ: {{ template "s6_rc_services_count" (args "inazuma" "default") }}</code>
+    <code>SK: {{ template "s6_rc_services_count" (args "shimakaze" "default") }}</code>
+  </div>
+  <div>
+    <h4>Traefik Latency</h4>
+    <div id="traefikLatencyGraph"></div>
+    <script>
+      new PromConsole.Graph({
+        node: document.querySelector("#traefikLatencyGraph"),
+        expr: [
+          `histogram_quantile(0.99, sum(rate(traefik_entrypoint_request_duration_seconds_bucket{entrypoint=~"https?"}[10m])) by (le, instance))`,
+        ],
+        name: '[[instance]]',
+        yAxisFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
+        yHoverFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
+        yTitle: "Latency (p99)",
+        yUnits: "s",
+      })
+    </script>
+  </div>
+  <div>
+    <h4>Node CPU Utilisation</h4>
+    <div id="cpuUtilGraph"></div>
+    <script>
+      new PromConsole.Graph({
+      node: document.querySelector("#cpuUtilGraph"),
+      expr: [
+        `(1 - avg(rate(node_cpu_seconds_total{mode="idle"}[1m]))) * 100.0`,
+      ],
+      name: '[[instance]]',
+      yAxisFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
+      yHoverFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
+      yUnits: "%",
+      min: 0,
+      max: 100,
+    })
+    </script>
+  </div>
+  <div>
+    <h4>Node CPU Saturation</h4>
+    <div id="cpuSatGraph"></div>
+    <script>
+      new PromConsole.Graph({
+      node: document.querySelector("#cpuSatGraph"),
+      expr: [
+        `sum by (instance) (node_load15) / count by (instance) (sum by (instance, cpu) (node_cpu_seconds_total))`,
+      ],
+      name: '[[instance]]',
+      yAxisFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
+      yHoverFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
+    })
+    </script>
+  </div>
+  <div>
+    <h4>Node Memory Utilisation</h4>
+    <div id="memUtilGraph"></div>
+    <script>
+      new PromConsole.Graph({
+      node: document.querySelector("#memUtilGraph"),
+      expr: [
+        `(1 - sum by (instance) (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes) / sum by (instance) (node_memory_MemTotal_bytes)) * 100.0`,
+      ],
+      name: '[[instance]]',
+      yAxisFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
+      yHoverFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
+      yUnits: "%",
+      min: 0,
+      max: 100,
+    })
+    </script>
+  </div>
+  <div>
+    <h4>Node Memory Saturation</h4>
+    <div id="memSatGraph"></div>
+    <script>
+      new PromConsole.Graph({
+      node: document.querySelector("#memSatGraph"),
+      expr: [
+        `1e3 * sum by (instance) (rate(node_vmstat_pgpgin[2m]) + rate(node_vmstat_pgpgout[2m]))`,
+      ],
+      name: '[[instance]]',
+      yAxisFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
+      yHoverFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
+    })
+    </script>
+  </div>
 </div>
 {{ template "prom_content_tail" . }}
 
diff --git a/bundles/prometheus/files/consoles/node-overview.html b/bundles/prometheus/files/consoles/node-overview.html
deleted file mode 100644
index edeca2e..0000000
--- a/bundles/prometheus/files/consoles/node-overview.html
+++ /dev/null
@@ -1,121 +0,0 @@
-{{ template "head" . }}
-
-{{ template "prom_right_table_head" }}
-  <tr><th colspan="2">Overview</th></tr>
-  <tr>
-    <td>User CPU</td>
-    <td>{{ template "prom_query_drilldown" (args (printf "sum(irate(node_cpu_seconds_total{job='node-exporter',instance='%s',mode='user'}[5m])) * 100 / count(count by (cpu)(node_cpu_seconds_total{job='node-exporter',instance='%s'}))" .Params.instance .Params.instance) "%" "printf.1f") }}</td>
-  </tr>
-  <tr>
-    <td>System CPU</td>
-    <td>{{ template "prom_query_drilldown" (args (printf "sum(irate(node_cpu_seconds_total{job='node-exporter',instance='%s',mode='system'}[5m])) * 100 / count(count by (cpu)(node_cpu_seconds_total{job='node-exporter',instance='%s'}))" .Params.instance .Params.instance) "%" "printf.1f") }}</td>
-  </tr>
-  <tr>
-    <td>Memory Total</td>
-    <td>{{ template "prom_query_drilldown" (args (printf "node_memory_MemTotal_bytes{job='node-exporter',instance='%s'}" .Params.instance) "B" "humanize1024") }}</td>
-  </tr>
-  <tr>
-    <td>Memory Free</td>
-    <td>{{ template "prom_query_drilldown" (args (printf "node_memory_MemFree_bytes{job='node-exporter',instance='%s'}" .Params.instance) "B" "humanize1024") }}</td>
-  </tr>
-<tr>
-  <th colspan="2">Network</th>
-</tr>
-{{ range printf "node_network_receive_bytes_total{job='node-exporter',instance='%s',device!='lo'}" .Params.instance | query | sortByLabel "device" }}
-  <tr>
-    <td>{{ .Labels.device }} Received</td>
-    <td>{{ template "prom_query_drilldown" (args (printf "irate(node_network_receive_bytes_total{job='node-exporter',instance='%s',device='%s'}[5m])" .Labels.instance .Labels.device) "B/s" "humanize") }}</td>
-  </tr>
-  <tr>
-    <td>{{ .Labels.device }} Transmitted</td>
-    <td>{{ template "prom_query_drilldown" (args (printf "irate(node_network_transmit_bytes_total{job='node-exporter',instance='%s',device='%s'}[5m])" .Labels.instance .Labels.device) "B/s" "humanize") }}</td>
-  </tr>
-{{ end }}
-<tr>
-  <th colspan="2">Disks</th>
-</tr>
-{{ range printf "node_disk_io_time_seconds_total{job='node-exporter',instance='%s',device!~'^(md\\\\d+$|dm-)'}" .Params.instance | query | sortByLabel "device" }}
-  <tr>
-    <td>{{ .Labels.device }} Utilization</td>
-    <td>{{ template "prom_query_drilldown" (args (printf "irate(node_disk_io_time_seconds_total{job='node-exporter',instance='%s',device='%s'}[5m]) * 100" .Labels.instance .Labels.device) "%" "printf.1f") }}</td>
-  </tr>
-{{ end }}
-{{ range printf "node_disk_io_time_seconds_total{job='node-exporter',instance='%s'}" .Params.instance | query | sortByLabel "device" }}
-  <tr>
-    <td>{{ .Labels.device }} Throughput</td>
-    <td>{{ template "prom_query_drilldown" (args (printf "irate(node_disk_read_bytes_total{job='node-exporter',instance='%s',device='%s'}[5m]) + irate(node_disk_written_bytes_total{job='node-exporter',instance='%s',device='%s'}[5m])" .Labels.instance .Labels.device .Labels.instance .Labels.device) "B/s" "humanize") }}</td>
-  </tr>
-{{ end }}
-<tr>
-  <th colspan="2">Filesystem Fullness</th>
-</tr>
-{{ define "roughlyNearZero" }}
-{{ if gt .1 . }}~0{{ else }}{{ printf "%.1f" . }}{{ end }}
-{{ end }}
-{{ range printf "node_filesystem_size_bytes{job='node-exporter',instance='%s'}" .Params.instance | query | sortByLabel "mountpoint" }}
-  <tr>
-    <td>{{ .Labels.mountpoint }}</td>
-    <td>{{ template "prom_query_drilldown" (args (printf "100 - node_filesystem_avail_bytes{job='node-exporter',instance='%s',mountpoint='%s'} / node_filesystem_size_bytes{job='node-exporter'} * 100" .Labels.instance .Labels.mountpoint) "%" "roughlyNearZero") }}</td>
-  </tr>
-{{ end }}
-
-{{ template "prom_right_table_tail" }}
-
-{{ template "prom_content_head" . }}
-  <h1>Node Overview - {{ reReplaceAll "(.*?://)([^:/]+?)(:\\d+)?/.*" "$2" .Params.instance }}</h1>
-
-  <h3>CPU Usage</h3>
-  <div id="cpuGraph"></div>
-  <script>
-  new PromConsole.Graph({
-    node: document.querySelector("#cpuGraph"),
-    expr: "sum by (mode)(irate(node_cpu_seconds_total{job='node-exporter',instance='{{ .Params.instance }}',mode!='idle',mode!='iowait',mode!='steal'}[5m]))",
-    renderer: 'area',
-    max: {{ with printf "count(count by (cpu)(node_cpu_seconds_total{job='node-exporter',instance='%s'}))" .Params.instance | query }}{{ . | first | value }}{{ else}}undefined{{end}},
-    yAxisFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
-    yHoverFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
-    yTitle: 'Cores'
-  })
-  </script>
-
-  <h3>Disk I/O Utilization</h3>
-  <div id="diskioGraph"></div>
-  <script>
-  new PromConsole.Graph({
-    node: document.querySelector("#diskioGraph"),
-    expr: [
-      "irate(node_disk_io_time_seconds_total{job='node-exporter',instance='{{ .Params.instance }}',device!~'^(md\\\\d+$|dm-)'}[5m]) * 100",
-    ],
-    min: 0,
-    name: '[[ device ]]',
-    yAxisFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
-    yHoverFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
-    yUnits: "%",
-    yTitle: 'Disk I/O Utilization'
-  })
-  </script>
-
-  <h3>Memory</h3>
-  <div id="memoryGraph"></div>
-  <script>
-  new PromConsole.Graph({
-    node: document.querySelector("#memoryGraph"),
-    renderer: 'area',
-    expr: [
-      "node_memory_Cached_bytes{job='node-exporter',instance='{{ .Params.instance }}'}",
-      "node_memory_Buffers_bytes{job='node-exporter',instance='{{ .Params.instance }}'}",
-      "node_memory_MemTotal_bytes{job='node-exporter',instance='{{ .Params.instance }}'} - node_memory_MemFree_bytes{job='node-exporter',instance='{{.Params.instance}}'} - node_memory_Buffers_bytes{job='node-exporter',instance='{{.Params.instance}}'} - node_memory_Cached_bytes{job='node-exporter',instance='{{.Params.instance}}'}",
-      "node_memory_MemFree{job='node-exporter',instance='{{ .Params.instance }}'}",
-    ],
-    name: ["Cached", "Buffers", "Used", "Free"],
-    min: 0,
-    yUnits: "B",
-    yAxisFormatter: PromConsole.NumberFormatter.humanize1024,
-    yHoverFormatter: PromConsole.NumberFormatter.humanize1024,
-    yTitle: 'Memory'
-  })
-  </script>
-
-{{ template "prom_content_tail" . }}
-
-{{ template "tail" }}
diff --git a/bundles/prometheus/files/consoles/node.html b/bundles/prometheus/files/consoles/node.html
new file mode 100644
index 0000000..9ef79ce
--- /dev/null
+++ b/bundles/prometheus/files/consoles/node.html
@@ -0,0 +1,133 @@
+{{/* vim: set ft=gotmpl: */}}
+
+{{ template "head" . }}
+
+{{ template "prom_content_head" . }}
+<div class="prom_content_div">
+  <div style="columns:2;">
+    <h1 style="margin-bottom:10px;">{{ reReplaceAll "(.*?://)([^:/]+?)(:\\d+)?/.*" "$2" .Params.instance }}</h1>
+    <table class="table">
+      <thead class="thead-dark">
+        <tr><th colspan="2">Overview</th></tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td>User CPU</td>
+          <td>{{ template "prom_query_drilldown" (args (printf "sum(irate(node_cpu_seconds_total{job='node-exporter',instance='%s',mode='user'}[5m])) * 100 / count(count by (cpu)(node_cpu_seconds_total{job='node-exporter',instance='%s'}))" .Params.instance .Params.instance) "%" "printf.1f") }}</td>
+        </tr>
+        <tr>
+          <td>System CPU</td>
+          <td>{{ template "prom_query_drilldown" (args (printf "sum(irate(node_cpu_seconds_total{job='node-exporter',instance='%s',mode='system'}[5m])) * 100 / count(count by (cpu)(node_cpu_seconds_total{job='node-exporter',instance='%s'}))" .Params.instance .Params.instance) "%" "printf.1f") }}</td>
+        </tr>
+        <tr>
+          <td>Memory Total</td>
+          <td>{{ template "prom_query_drilldown" (args (printf "node_memory_MemTotal_bytes{job='node-exporter',instance='%s'}" .Params.instance) "B" "humanize1024") }}</td>
+        </tr>
+        <tr>
+          <td>Memory Free</td>
+          <td>{{ template "prom_query_drilldown" (args (printf "node_memory_MemFree_bytes{job='node-exporter',instance='%s'}" .Params.instance) "B" "humanize1024") }}</td>
+        </tr>
+      </tbody>
+    </table>
+    <table class="table">
+      <tr><th colspan="2">Network</th></tr>
+      {{ range printf "node_network_receive_bytes_total{job='node-exporter',instance='%s',device!='lo'}" .Params.instance | query | sortByLabel "device" }}
+      <tr>
+        <td>{{ .Labels.device }} Received</td>
+        <td>{{ template "prom_query_drilldown" (args (printf "irate(node_network_receive_bytes_total{job='node-exporter',instance='%s',device='%s'}[5m])" .Labels.instance .Labels.device) "B/s" "humanize") }}</td>
+      </tr>
+      <tr>
+        <td>{{ .Labels.device }} Transmitted</td>
+        <td>{{ template "prom_query_drilldown" (args (printf "irate(node_network_transmit_bytes_total{job='node-exporter',instance='%s',device='%s'}[5m])" .Labels.instance .Labels.device) "B/s" "humanize") }}</td>
+      </tr>
+      {{ end }}
+    </table>
+    <table class="table">
+      <tr><th colspan="2">Disks</th></tr>
+      {{ range printf "node_disk_io_time_seconds_total{job='node-exporter',instance='%s',device!~'^(md\\\\d+$|dm-)'}" .Params.instance | query | sortByLabel "device" }}
+      <tr>
+        <td>{{ .Labels.device }} Utilization</td>
+        <td>{{ template "prom_query_drilldown" (args (printf "irate(node_disk_io_time_seconds_total{job='node-exporter',instance='%s',device='%s'}[5m]) * 100" .Labels.instance .Labels.device) "%" "printf.1f") }}</td>
+      </tr>
+      {{ end }}
+      {{ range printf "node_disk_io_time_seconds_total{job='node-exporter',instance='%s'}" .Params.instance | query | sortByLabel "device" }}
+      <tr>
+        <td>{{ .Labels.device }} Throughput</td>
+        <td>{{ template "prom_query_drilldown" (args (printf "irate(node_disk_read_bytes_total{job='node-exporter',instance='%s',device='%s'}[5m]) + irate(node_disk_written_bytes_total{job='node-exporter',instance='%s',device='%s'}[5m])" .Labels.instance .Labels.device .Labels.instance .Labels.device) "B/s" "humanize") }}</td>
+      </tr>
+      {{ end }}
+    </table>
+    <table class="table">
+      <tr><th colspan="2">Filesystem Fullness</th></tr>
+      {{ define "roughlyNearZero" }}
+      {{ if gt .1 . }}~0{{ else }}{{ printf "%.1f" . }}{{ end }}
+      {{ end }}
+      {{ range printf "node_filesystem_size_bytes{job='node-exporter',instance='%s'}" .Params.instance | query | sortByLabel "mountpoint" }}
+      <tr>
+        <td>{{ .Labels.mountpoint }}</td>
+        <td>{{ template "prom_query_drilldown" (args (printf "100 - node_filesystem_avail_bytes{job='node-exporter',instance='%s',mountpoint='%s'} / node_filesystem_size_bytes{job='node-exporter'} * 100" .Labels.instance .Labels.mountpoint) "%" "roughlyNearZero") }}</td>
+      </tr>
+      {{ end }}
+    </table>
+  </div>
+  <div>
+    <h3>CPU Usage</h3>
+    <div id="cpuGraph"></div>
+    <script>
+      new PromConsole.Graph({
+        node: document.querySelector("#cpuGraph"),
+        expr: [
+          `sum by (mode)(irate(node_cpu_seconds_total{job='node-exporter',instance='{{ .Params.instance }}',mode!='idle',mode!='iowait',mode!='steal'}[5m]))`,
+        ],
+        renderer: 'area',
+        max: {{ with printf "count(count by (cpu)(node_cpu_seconds_total{job='node-exporter',instance='%s'}))" .Params.instance | query }}{{ . | first | value }}{{ else}}undefined{{end}},
+        yAxisFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
+        yHoverFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
+        yTitle: 'Cores'
+      })
+    </script>
+  </div>
+  <div>
+    <h3>Disk I/O Utilization</h3>
+    <div id="diskioGraph"></div>
+    <script>
+      new PromConsole.Graph({
+        node: document.querySelector("#diskioGraph"),
+        expr: [
+          `irate(node_disk_io_time_seconds_total{job='node-exporter',instance='{{ .Params.instance }}',device!~'^(md\\\\d+$|dm-)'}[5m]) * 100`,
+        ],
+        min: 0,
+        name: '[[ device ]]',
+        yAxisFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
+        yHoverFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
+        yUnits: "%",
+        yTitle: 'Disk I/O Utilization'
+      })
+    </script>
+  </div>
+  <div>
+    <h3>Memory</h3>
+    <div id="memoryGraph"></div>
+    <script>
+      new PromConsole.Graph({
+        node: document.querySelector("#memoryGraph"),
+        renderer: 'area',
+        expr: [
+          `node_memory_Cached_bytes{job='node-exporter',instance='{{ .Params.instance }}'}`,
+          `node_memory_Buffers_bytes{job='node-exporter',instance='{{ .Params.instance }}'}`,
+          `node_memory_MemTotal_bytes{job='node-exporter',instance='{{ .Params.instance }}'} - node_memory_MemFree_bytes{job='node-exporter',instance='{{.Params.instance}}'} - node_memory_Buffers_bytes{job='node-exporter',instance='{{.Params.instance}}'} - node_memory_Cached_bytes{job='node-exporter',instance='{{.Params.instance}}'}`,
+          `node_memory_MemFree{job='node-exporter',instance='{{ .Params.instance }}'}`,
+        ],
+        name: ["Cached", "Buffers", "Used", "Free"],
+        min: 0,
+        yUnits: "B",
+        yAxisFormatter: PromConsole.NumberFormatter.humanize1024,
+        yHoverFormatter: PromConsole.NumberFormatter.humanize1024,
+        yTitle: 'Memory'
+      })
+    </script>
+  </div>
+</div>
+{{ template "prom_content_tail" . }}
+
+{{ template "tail" . }}
diff --git a/bundles/prometheus/files/consoles/prom.html b/bundles/prometheus/files/consoles/prom.html
new file mode 100644
index 0000000..4bc5eb7
--- /dev/null
+++ b/bundles/prometheus/files/consoles/prom.html
@@ -0,0 +1,88 @@
+{{/* vim: set ft=gotmpl: */}}
+
+{{ template "head" . }}
+
+{{ template "prom_content_head" . }}
+<div class="prom_content_div">
+  <div style="columns:2;">
+    <h1 style="margin-bottom:10px;">Prometheus</h1>
+    <table>
+      <tr><th colspan="2">Overview</th></tr>
+      <tr>
+        <td>CPU</td>
+        <td>{{ template "prom_query_drilldown" (args (printf "irate(process_cpu_seconds_total{job='prometheus',instance='%s'}[5m])" .Params.instance) "s/s" "humanizeNoSmallPrefix") }}</td>
+      </tr>
+      <tr>
+        <td>Memory</td>
+        <td>{{ template "prom_query_drilldown" (args (printf "process_resident_memory_bytes{job='prometheus',instance='%s'}" .Params.instance) "B" "humanize1024") }}</td>
+      </tr>
+      <tr>
+        <td>Version</td>
+        <td>{{ with query (printf "prometheus_build_info{job='prometheus',instance='%s'}" .Params.instance) }}{{. | first | label "version"}}{{end}}</td>
+      </tr>
+    </table>
+    <table>
+      <tr><th colspan="2">Storage</th></tr>
+      <tr>
+        <td>Ingested Samples</td>
+        <td>{{ template "prom_query_drilldown" (args (printf "irate(prometheus_tsdb_head_samples_appended_total{job='prometheus',instance='%s'}[5m])" .Params.instance) "/s" "humanizeNoSmallPrefix") }}</td>
+      </tr>
+      <tr>
+        <td>Head Series</td>
+        <td>{{ template "prom_query_drilldown" (args (printf "prometheus_tsdb_head_series{job='prometheus',instance='%s'}" .Params.instance) "" "humanize") }}</td>
+      </tr>
+      <tr>
+        <td>Blocks Loaded</td>
+        <td>{{ template "prom_query_drilldown" (args (printf "prometheus_tsdb_blocks_loaded{job='prometheus',instance='%s'}" .Params.instance) "" "humanize") }}</td>
+      </tr>
+    </table>
+    <table>
+      <tr><th colspan="2">Rules</th></tr>
+      <tr>
+        <td>Evaluation Duration</td>
+        <td>{{ template "prom_query_drilldown" (args (printf "irate(prometheus_evaluator_duration_seconds_sum{job='prometheus',instance='%s'}[5m]) / irate(prometheus_evaluator_duration_seconds_count{job='prometheus',instance='%s'}[5m])" .Params.instance .Params.instance) "" "humanizeDuration") }}</td>
+      </tr>
+      <tr>
+        <td>Notification Latency</td>
+        <td>{{ template "prom_query_drilldown" (args (printf "irate(prometheus_notifications_latency_seconds_sum{job='prometheus',instance='%s'}[5m]) / irate(prometheus_notifications_latency_seconds_count{job='prometheus',instance='%s'}[5m])" .Params.instance .Params.instance) "" "humanizeDuration") }}</td>
+      </tr>
+      <tr>
+        <td>Notification Queue</td>
+        <td>{{ template "prom_query_drilldown" (args (printf "prometheus_notifications_queue_length{job='prometheus',instance='%s'}" .Params.instance) "" "humanize") }}</td>
+      </tr>
+    </table>
+  </div>
+  <div>
+    <h3>Ingested Samples</h3>
+    <div id="samplesGraph"></div>
+    <script>
+      new PromConsole.Graph({
+      node: document.querySelector("#samplesGraph"),
+      expr: "irate(prometheus_tsdb_head_samples_appended_total{job='prometheus',instance='{{ .Params.instance }}'}[5m])",
+      name: 'Ingested Samples',
+      yAxisFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
+      yHoverFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
+      yTitle: "Samples",
+      yUnits: "/s",
+    })
+    </script>
+  </div>
+  <div>
+    <h3>HTTP Server</h3>
+    <div id="serverGraph"></div>
+    <script>
+      new PromConsole.Graph({
+      node: document.querySelector("#serverGraph"),
+      expr: "irate(prometheus_http_request_duration_seconds_count{job='prometheus',instance='{{ .Params.instance }}'}[5m])",
+      name: '[[handler]]',
+      yAxisFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
+      yHoverFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
+      yTitle: "Requests",
+      yUnits: "/s",
+    })
+    </script>
+  </div>
+</div>
+{{ template "prom_content_tail" . }}
+
+{{ template "tail" }}
diff --git a/bundles/prometheus/files/consoles/prometheus-overview.html b/bundles/prometheus/files/consoles/prometheus-overview.html
deleted file mode 100644
index 08e027d..0000000
--- a/bundles/prometheus/files/consoles/prometheus-overview.html
+++ /dev/null
@@ -1,96 +0,0 @@
-{{ template "head" . }}
-
-{{ template "prom_right_table_head" }}
-  <tr>
-    <th colspan="2">Overview</th>
-  </tr>
-  <tr>
-    <td>CPU</td>
-    <td>{{ template "prom_query_drilldown" (args (printf "irate(process_cpu_seconds_total{job='prometheus',instance='%s'}[5m])" .Params.instance) "s/s" "humanizeNoSmallPrefix") }}</td>
-  </tr>
-  <tr>
-    <td>Memory</td>
-    <td>{{ template "prom_query_drilldown" (args (printf "process_resident_memory_bytes{job='prometheus',instance='%s'}" .Params.instance) "B" "humanize1024") }}</td>
-  </tr>
-  <tr>
-    <td>Version</td>
-    <td>{{ with query (printf "prometheus_build_info{job='prometheus',instance='%s'}" .Params.instance) }}{{. | first | label "version"}}{{end}}</td>
-  </tr>
-
-  <tr>
-    <th colspan="2">Storage</th>
-  </tr>
-  <tr>
-    <td>Ingested Samples</td>
-    <td>{{ template "prom_query_drilldown" (args (printf "irate(prometheus_tsdb_head_samples_appended_total{job='prometheus',instance='%s'}[5m])" .Params.instance) "/s" "humanizeNoSmallPrefix") }}</td>
-  </tr>
-  <tr>
-    <td>Head Series</td>
-    <td>{{ template "prom_query_drilldown" (args (printf "prometheus_tsdb_head_series{job='prometheus',instance='%s'}" .Params.instance) "" "humanize") }}</td>
-  </tr>
-  <tr>
-    <td>Blocks Loaded</td>
-    <td>{{ template "prom_query_drilldown" (args (printf "prometheus_tsdb_blocks_loaded{job='prometheus',instance='%s'}" .Params.instance) "" "humanize") }}</td>
-  </tr>
-  <tr>
-    <th colspan="2">Rules</th>
-  </tr>
-  <tr>
-    <td>Evaluation Duration</td>
-    <td>{{ template "prom_query_drilldown" (args (printf "irate(prometheus_evaluator_duration_seconds_sum{job='prometheus',instance='%s'}[5m]) / irate(prometheus_evaluator_duration_seconds_count{job='prometheus',instance='%s'}[5m])" .Params.instance .Params.instance) "" "humanizeDuration") }}</td>
-  </tr>
-  <tr>
-    <td>Notification Latency</td>
-    <td>{{ template "prom_query_drilldown" (args (printf "irate(prometheus_notifications_latency_seconds_sum{job='prometheus',instance='%s'}[5m]) / irate(prometheus_notifications_latency_seconds_count{job='prometheus',instance='%s'}[5m])" .Params.instance .Params.instance) "" "humanizeDuration") }}</td>
-  </tr>
-  <tr>
-    <td>Notification Queue</td>
-    <td>{{ template "prom_query_drilldown" (args (printf "prometheus_notifications_queue_length{job='prometheus',instance='%s'}" .Params.instance) "" "humanize") }}</td>
-  </tr>
-  <tr>
-    <th colspan="2">HTTP Server</th>
-  </tr>
-{{ range printf "prometheus_http_request_duration_seconds_count{job='prometheus',instance='%s'}" .Params.instance | query | sortByLabel "handler" }}
-  <tr>
-    <td>{{ .Labels.handler }}</td>
-    <td>{{ template "prom_query_drilldown" (args (printf "irate(prometheus_http_request_duration_seconds_count{job='prometheus',instance='%s',handler='%s'}[5m])" .Labels.instance .Labels.handler) "/s" "humanizeNoSmallPrefix") }}</td>
-  </tr>
-{{ end }}
-
-{{ template "prom_right_table_tail" }}
-
-{{ template "prom_content_head" . }}
- <div class="prom_content_div">
-  <h1>Prometheus Overview - {{ .Params.instance }}</h1>
-
-  <h3>Ingested Samples</h3>
-  <div id="samplesGraph"></div>
-  <script>
-  new PromConsole.Graph({
-    node: document.querySelector("#samplesGraph"),
-    expr: "irate(prometheus_tsdb_head_samples_appended_total{job='prometheus',instance='{{ .Params.instance }}'}[5m])",
-    name: 'Ingested Samples',
-    yAxisFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
-    yHoverFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
-    yTitle: "Samples",
-    yUnits: "/s",
-  })
-  </script>
-
-  <h3>HTTP Server</h3>
-  <div id="serverGraph"></div>
-  <script>
-  new PromConsole.Graph({
-    node: document.querySelector("#serverGraph"),
-    expr: "irate(prometheus_http_request_duration_seconds_count{job='prometheus',instance='{{ .Params.instance }}'}[5m])",
-    name: '[[handler]]',
-    yAxisFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
-    yHoverFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
-    yTitle: "Requests",
-    yUnits: "/s",
-  })
-  </script>
- </div>
-{{ template "prom_content_tail" . }}
-
-{{ template "tail" }}