Loading

Rule grouping in the experimental alerting system

Rule grouping is an optional setting in the experimental alerting system that lets a single rule track multiple things independently. For example, a rule monitoring CPU usage across hosts can produce a separate alert series for each host, rather than one alert for everything combined.

In Alert mode, each group becomes its own alert episode with an independent lifecycle. One group can be active while another has recovered, and notifications apply per episode, not across all groups combined. Snooze state is also per series. Snoozing one group does not affect other groups tracked by the same rule.

Configure grouping when:

  • Your ES|QL query uses a BY clause to aggregate across multiple subjects such as hosts, services, or users, and you want each subject to have an independent alert lifecycle.
  • You need to track that one host has recovered while another is still breaching, rather than treating all subjects as a single combined series.
  • You want action policy notifications to apply per subject rather than firing once for the entire rule.

Skip grouping when:

  • Your query does not use a BY clause. Grouping requires BY columns in the query output to be meaningful.
  • You intentionally want a single alert series for the rule regardless of how many subjects match. An example is a rule that fires when any host in a cluster is down and the individual host identity doesn't matter for the notification.

Rule grouping controls how alert series are created. Notification grouping, configured on an action policy, controls how those alert episodes are batched into messages. These are separate settings.

The experimental alerting system does not automatically infer grouping from your ES|QL query. When your query uses BY to produce one row per group, the system still treats all rows as a single series unless you explicitly declare which fields define series identity in the grouping configuration. The fields you declare in grouping.fields are what the system uses to separate rows into independent alert series and track each one through its own lifecycle.

The fields you declare in grouping.fields must match the column names produced by the BY clause in your ES|QL STATS command. If they don't match, the system can't correlate query rows to alert series and the grouping configuration has no effect.

Tip

Write the query first, then set the group fields. That way the BY columns are already defined and you can select them directly. If you later add or remove a BY field in the query, update the group fields to match.

This rule counts HTTP errors per service and opens a separate alert series for each service that exceeds the threshold. Each service gets its own lifecycle. If the checkout service recovers but the payments service stays critical, those are tracked independently.

FROM logs-*
| WHERE @timestamp >= ?_tstart AND @timestamp < ?_tend
| STATS error_count = COUNT_IF(http.response.status_code >= 500) BY service.name
| WHERE error_count > 10
| KEEP service.name, error_count
		
  1. Group field: one series per service

Without a matching grouping.fields entry, the rule treats all services as a single combined series. A spike in one service would activate the alert for everything, and recovery requires all services to drop below the threshold at the same time.

When the query groups by multiple fields, include all fields in grouping.fields to create one alert series per unique combination.

FROM metrics-*
| WHERE @timestamp >= ?_tstart AND @timestamp < ?_tend
| STATS avg_cpu = AVG(system.cpu.total.pct) BY host.name, cloud.region
| WHERE avg_cpu > 0.90
| KEEP host.name, cloud.region, avg_cpu
		
  1. Group fields: one series per host+region pair