Skip to main content

Rules And Alerts Migration

This project treats alerting and ruler compatibility as a read path:

  • vmalert executes alerting and recording rules against VictoriaLogs
  • Loki-compatible read endpoints on the proxy expose those rules and alerts back to Grafana
  • the proxy does not implement Loki ruler write APIs

If you already have Loki rule files, use the migration tool to convert them into vmalert rule files.

Migration Model

Target shape:

  1. Keep Grafana dashboards and Explore on the Loki datasource that points to the proxy.
  2. Run vmalert for rules and alerts.
  3. Point vmalert at VictoriaLogs.
  4. Point the proxy -ruler-backend and -alerts-backend at vmalert.
  5. Let Grafana read rules and alerts through Loki-compatible endpoints on the proxy.

This gives you:

  • Loki-compatible rule and alert visibility in Grafana
  • VictoriaLogs-native rule execution
  • no write-side botched Loki ruler emulation

Tooling

Convert a Loki-style rule file:

go run ./cmd/rules-migrate -in loki-rules.yaml -out vmalert-rules.yaml

Read from stdin and write to stdout:

cat loki-rules.yaml | go run ./cmd/rules-migrate > vmalert-rules.yaml

Supported inputs:

  • Prometheus/Loki-style groups: rule files
  • legacy Loki YAML maps such as the classic /loki/api/v1/rules shape

Output:

  • groups: YAML
  • every group forced to type: vlogs
  • every rule expr translated from LogQL into LogsQL

Example

Input:

groups:
- name: api
interval: 1m
rules:
- alert: HighErrorRate
expr: sum by (app) (rate({app="api-gateway"} |= "error" [5m]))
for: 2m
labels:
severity: page

Output:

groups:
- name: api
interval: 1m
type: vlogs
rules:
- alert: HighErrorRate
expr: '{app="api-gateway"} ~"error" | stats rate() as value by (app)'
for: 2m
labels:
severity: page

The exact generated expression depends on the translator, but the important part is that the output is now valid vmalert + VictoriaLogs rule YAML.

Deploying vmalert

Minimal example:

services:
vmalert:
image: victoriametrics/vmalert:v1.138.0
command:
- -datasource.url=http://victorialogs:9428
- -rule=/rules/vmalert-rules.yaml
- -rule.defaultRuleType=vlogs
- -notifier.url=http://alertmanager:9093

For local validation without a real Alertmanager:

services:
vmalert:
image: victoriametrics/vmalert:v1.138.0
command:
- -datasource.url=http://victorialogs:9428
- -rule=/rules/vmalert-rules.yaml
- -rule.defaultRuleType=vlogs
- -notifier.blackhole

If your file contains recording rules (record:), add a remote-write target so vmalert can persist those series:

services:
vmalert:
image: victoriametrics/vmalert:v1.138.0
command:
- -datasource.url=http://victorialogs:9428
- -rule=/rules/vmalert-rules.yaml
- -rule.defaultRuleType=vlogs
- -remoteWrite.url=http://victoriametrics:8428/api/v1/write
- -notifier.blackhole

Point the proxy at vmalert:

./loki-vl-proxy \
-backend=http://victorialogs:9428 \
-ruler-backend=http://vmalert:8880 \
-alerts-backend=http://vmalert:8880

Grafana Behavior

After vmalert is configured and the proxy points at it:

  • /loki/api/v1/rules returns classic Loki-compatible YAML
  • /api/prom/rules returns the same classic YAML alias
  • /prometheus/api/v1/rules returns Prometheus-style JSON
  • /loki/api/v1/alerts, /api/prom/alerts, and /prometheus/api/v1/alerts return JSON alert state
  • Grafana datasource proxy reads stay consistent with direct paths when datasource_type=vlogs is set:
    • /api/datasources/proxy/uid/<uid>/prometheus/api/v1/rules?datasource_type=vlogs
    • /api/datasources/proxy/uid/<uid>/prometheus/api/v1/alerts?datasource_type=vlogs

So Grafana can keep reading alert/rule state from the Loki-facing datasource path.

What Converts Cleanly

These rule patterns generally migrate well:

  • stream selectors
  • line filters
  • | json
  • | logfmt
  • basic metric functions like rate, count_over_time, sum_over_time
  • normal sum by (...), topk(...), and similar aggregation patterns already covered by the translator

What Needs Review

The migration tool now defaults to a hardened mode:

  • safe rules are converted automatically
  • risky rules fail conversion with a specific manual-review error
  • you can generate a review report with -report
  • you can override the safety guard with -allow-risky if you intentionally want translated output plus warnings

Example:

go run ./cmd/rules-migrate \
-in loki-rules.yaml \
-out vmalert-rules.yaml \
-report migration-review.txt

If you intentionally want translated output even for risky rules:

go run ./cmd/rules-migrate \
-in loki-rules.yaml \
-out vmalert-rules.yaml \
-report migration-review.txt \
-allow-risky

The migration tool uses the translator, not the proxy runtime.

That means proxy-only runtime emulation does not automatically exist in vmalert rules. Review converted rules if they rely on:

  • without()
  • on() / ignoring()
  • group_left() / group_right()
  • subquery [range:step]
  • histogram() recording rules
  • other behaviors that the proxy currently emulates above VictoriaLogs rather than translating directly into a backend-native equivalent

For those cases:

  1. convert the file
  2. review the translated expr
  3. validate it directly against VictoriaLogs or vmalert
  4. simplify or rewrite the rule if needed

Validation Workflow

Recommended migration flow:

  1. Convert the Loki rule file with cmd/rules-migrate.
  2. Start vmalert with the generated file.
  3. Call GET /api/v1/rules?datasource_type=vlogs on vmalert.
  4. Point the proxy at vmalert.
  5. Verify:
    • GET /prometheus/api/v1/rules
    • GET /prometheus/api/v1/alerts
    • GET /loki/api/v1/rules
    • GET /api/datasources/proxy/uid/<uid>/prometheus/api/v1/rules?datasource_type=vlogs (from Grafana)
    • GET /api/datasources/proxy/uid/<uid>/prometheus/api/v1/alerts?datasource_type=vlogs (from Grafana)
  6. Confirm Grafana sees the rules/alerts through the Loki datasource path.

Real-Life Test Coverage

The compose-backed compatibility stack already validates this model with a live backend:

  • VictoriaLogs
  • vmalert
  • Loki-VL-proxy
  • Grafana

See: