openvibers/openvibe.host Commits

Alert rules (Track O): deploy/prometheus/openvibe-rules.yml (target down 5 min, 5xx above 5% for 10 min, root disk under 10%, memory under 8%), loaded through rule_files in the rendered config; firing alerts at 127.0.0.1:9090/api/v1/alerts

OP OpenVibers committed 24 Sept 2026 · 25e6fc0277 · 3 files, +37 −1


Changes

ddiff --git a/deploy/prometheus/openvibe-rules.yml b/deploy/prometheus/openvibe-rules.yml
nnew file mode 100644
iindex 0000000..daa6f32
---- /dev/null
++++ b/deploy/prometheus/openvibe-rules.yml
@@@ -0,0 +1,32 @@
++# OpenVibe alert rules (roadmap Track O), loaded by the host's Prometheus (rule_files in the config
++# scripts/prometheus-config.js renders). Firing alerts are read at GET 127.0.0.1:9090/api/v1/alerts;
++# no notifier is configured yet.
++groups:
++ - name: openvibe
++ rules:
++ - alert: TargetDown
++ expr: up == 0
++ for: 5m
++ labels: { severity: page }
++ annotations:
++ summary: "{{ $labels.process }} ({{ $labels.instance }}) has not answered /metrics for 5 minutes"
++ - alert: HighErrorRate
++ expr: |
++ sum by (service) (rate(http_requests_total{status=~"5.."}[10m]))
++ / clamp_min(sum by (service) (rate(http_requests_total[10m])), 0.05) > 0.05
++ for: 10m
++ labels: { severity: ticket }
++ annotations:
++ summary: "{{ $labels.service }}: more than 5% of requests answered 5xx for 10 minutes"
++ - alert: DiskAlmostFull
++ expr: node_filesystem_avail_bytes{mountpoint="/",fstype!~"tmpfs|overlay"} / node_filesystem_size_bytes{mountpoint="/",fstype!~"tmpfs|overlay"} < 0.10
++ for: 10m
++ labels: { severity: page }
++ annotations:
++ summary: "Root filesystem has less than 10% free ({{ $value | humanizePercentage }})"
++ - alert: MemoryLow
++ expr: node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes < 0.08
++ for: 10m
++ labels: { severity: ticket }
++ annotations:
++ summary: "Less than 8% of memory available for 10 minutes"
ddiff --git a/scripts/prometheus-config.js b/scripts/prometheus-config.js
iindex 608df74..cced71b 100644
---- a/scripts/prometheus-config.js
++++ b/scripts/prometheus-config.js
@@@ -8,7 +8,8 @@
* "sites": { …, "metrics": false } nothing to scrape
*
* Every target is 127.0.0.1 (the services answer /metrics only to direct loopback callers), and
-- * node-exporter (host CPU, memory, disk) is scraped on 127.0.0.1:9100.
++ * node-exporter (host CPU, memory, disk) is scraped on 127.0.0.1:9100. Alert rules are read from
++ * /etc/prometheus/rules/*.yml (deploy/prometheus/openvibe-rules.yml).
*
* node scripts/prometheus-config.js [--inventory /etc/openvibe/host.json] [--interval 30s] > prometheus.yml
*/
@@@ -40,6 +41,8 @@ function render(inventory, { interval = '30s' } = {}) {
'global:',
` scrape_interval: ${interval}`,
' scrape_timeout: 10s',
++ 'rule_files:',
++ " - '/etc/prometheus/rules/*.yml'",
'scrape_configs:',
' - job_name: node',
' static_configs:',
ddiff --git a/test/prometheus-config.test.js b/test/prometheus-config.test.js
iindex 92e019d..fe4a643 100644
---- a/test/prometheus-config.test.js
++++ b/test/prometheus-config.test.js
@@@ -11,6 +11,7 @@ const out = render({ services: {
noport: {},
} }, { interval: '15s' });
assert.ok(out.includes('scrape_interval: 15s'));
++assert.ok(out.includes("rule_files:\n - '/etc/prometheus/rules/*.yml'"), 'alert rules');
assert.ok(out.includes("- targets: ['127.0.0.1:9100']"), 'node-exporter');
assert.ok(out.includes(" - job_name: live\n metrics_path: /metrics\n static_configs:\n - targets: ['127.0.0.1:3000']\n labels:\n service: live\n process: live\n"));
assert.ok(out.includes(" - targets: ['127.0.0.1:4012']\n labels:\n service: tools\n process: tools-img\n"), 'declared extra process');