# Grafana alerting provisioning — alert rules # Covers: runner down, high task failure rate, audio error spike, backend error spike. apiVersion: 1 groups: - orgId: 1 name: LibNovel Runner folder: LibNovel interval: 1m rules: - uid: runner-down title: Runner Down condition: C for: 2m annotations: summary: "LibNovel runner is not reachable" description: "The Prometheus scrape of runner:9091 has been failing for >2 minutes. Tasks are not being processed." labels: severity: critical service: runner data: - refId: A datasourceUid: prometheus relativeTimeRange: { from: 300, to: 0 } model: expr: "up{job=\"libnovel-runner\"}" instant: true intervalMs: 1000 maxDataPoints: 43200 - refId: C datasourceUid: __expr__ relativeTimeRange: { from: 300, to: 0 } model: type: classic_conditions conditions: - evaluator: { params: [1], type: lt } operator: { type: and } query: { params: [A] } reducer: { params: [], type: last } - uid: runner-high-failure-rate title: Runner High Task Failure Rate condition: C for: 5m annotations: summary: "Runner task failure rate is above 20%" description: "More than 20% of runner tasks have been failing for the last 5 minutes. Check runner logs." labels: severity: warning service: runner data: - refId: A datasourceUid: prometheus relativeTimeRange: { from: 600, to: 0 } model: expr: "rate(libnovel_runner_tasks_failed_total[5m]) / clamp_min(rate(libnovel_runner_tasks_completed_total[5m]) + rate(libnovel_runner_tasks_failed_total[5m]), 0.001)" instant: true intervalMs: 1000 maxDataPoints: 43200 - refId: C datasourceUid: __expr__ relativeTimeRange: { from: 600, to: 0 } model: type: classic_conditions conditions: - evaluator: { params: [0.2], type: gt } operator: { type: and } query: { params: [A] } reducer: { params: [], type: last } - uid: runner-tasks-stalled title: Runner Tasks Stalled condition: C for: 10m annotations: summary: "Runner has tasks running for >10 minutes with no completions" description: "tasks_running > 0 but rate(tasks_completed) is 0. Tasks may be stuck or the runner is in a crash loop." labels: severity: warning service: runner data: - refId: Running datasourceUid: prometheus relativeTimeRange: { from: 900, to: 0 } model: expr: "libnovel_runner_tasks_running" instant: true intervalMs: 1000 maxDataPoints: 43200 - refId: Rate datasourceUid: prometheus relativeTimeRange: { from: 900, to: 0 } model: expr: "rate(libnovel_runner_tasks_completed_total[10m])" instant: true intervalMs: 1000 maxDataPoints: 43200 - refId: C datasourceUid: __expr__ relativeTimeRange: { from: 900, to: 0 } model: type: classic_conditions conditions: - evaluator: { params: [0], type: gt } operator: { type: and } query: { params: [Running] } reducer: { params: [], type: last } - evaluator: { params: [0.001], type: lt } operator: { type: and } query: { params: [Rate] } reducer: { params: [], type: last } - orgId: 1 name: LibNovel Backend folder: LibNovel interval: 1m rules: - uid: backend-high-error-rate title: Backend High Error Rate condition: C for: 5m annotations: summary: "Backend API error rate above 5%" description: "More than 5% of backend HTTP requests are returning 5xx status codes (as seen from UI OTel instrumentation)." labels: severity: warning service: backend data: - refId: A datasourceUid: prometheus relativeTimeRange: { from: 600, to: 0 } model: expr: "sum(rate(http_client_request_duration_seconds_count{job=\"ui\", server_address=\"backend\", http_response_status_code=~\"5..\"}[5m])) / clamp_min(sum(rate(http_client_request_duration_seconds_count{job=\"ui\", server_address=\"backend\"}[5m])), 0.001)" instant: true intervalMs: 1000 maxDataPoints: 43200 - refId: C datasourceUid: __expr__ relativeTimeRange: { from: 600, to: 0 } model: type: classic_conditions conditions: - evaluator: { params: [0.05], type: gt } operator: { type: and } query: { params: [A] } reducer: { params: [], type: last } - uid: backend-high-p95-latency title: Backend High p95 Latency condition: C for: 5m annotations: summary: "Backend p95 latency above 2s" description: "95th percentile latency of backend spans has exceeded 2 seconds for >5 minutes." labels: severity: warning service: backend data: - refId: A datasourceUid: prometheus relativeTimeRange: { from: 600, to: 0 } model: expr: "histogram_quantile(0.95, sum(rate(traces_spanmetrics_latency_bucket{service=\"backend\"}[5m])) by (le))" instant: true intervalMs: 1000 maxDataPoints: 43200 - refId: C datasourceUid: __expr__ relativeTimeRange: { from: 600, to: 0 } model: type: classic_conditions conditions: - evaluator: { params: [2], type: gt } operator: { type: and } query: { params: [A] } reducer: { params: [], type: last } - orgId: 1 name: LibNovel OTel Pipeline folder: LibNovel interval: 2m rules: - uid: otel-collector-down title: OTel Collector Down condition: C for: 3m annotations: summary: "OTel collector is not reachable" description: "Prometheus cannot scrape otel-collector:8888. Traces and logs may be dropping." labels: severity: warning service: otel-collector data: - refId: A datasourceUid: prometheus relativeTimeRange: { from: 600, to: 0 } model: expr: "up{job=\"otel-collector\"}" instant: true intervalMs: 1000 maxDataPoints: 43200 - refId: C datasourceUid: __expr__ relativeTimeRange: { from: 600, to: 0 } model: type: classic_conditions conditions: - evaluator: { params: [1], type: lt } operator: { type: and } query: { params: [A] } reducer: { params: [], type: last }