<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title><![CDATA[Jason Rhodes - Elastic Observability Labs]]></title>
    <description><![CDATA[Trusted security news & research from the team at Elastic.]]></description>
    <copyright><![CDATA[© 2026. Elasticsearch B.V. All Rights Reserved]]></copyright>
    <image>
      <title><![CDATA[Jason Rhodes - Elastic Observability Labs]]></title>
      <url>https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltad972c1c27dbefc6/6a88d9782904ea5e8511d473/observability-labs-thumbnail.png</url>
      <link>https://www.elastic.co/observability-labs/author/jason-rhodes</link>
    </image>
    <link>https://www.elastic.co/observability-labs/author/jason-rhodes</link>
    <atom:link href="https://www.elastic.co/observability-labs/rss/author/jason-rhodes.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[en]]></language>
    <lastBuildDate>Tue, 20 Oct 2026 18:44:52 GMT</lastBuildDate>
  <item>
    <title><![CDATA[Control how alerts activate, recover and notify: Kibana Universal Alerting in Elastic Observability]]></title>
    <description><![CDATA[Turn ES|QL queries into alert data using Kibana Universal Alerting, and triage Classic and external alerts alongside Universal alerts in a single view.]]></description>
    <content:encoded><![CDATA[<p>Kibana Universal Alerting is a new option for building and maintaining your alerts and notifications. Whether you start with a guided builder or dive straight into ES|QL, you choose exactly what to query, which data each alert stores, and who gets notified across all of your rules. Every alert is stored as time-series data, allowing you to search, analyze, and triage them all in one place. Six levers, covered below, help you tune each step.</p>
<p>Kibana Universal Alerting will be generally available and on by default in Elastic Cloud Serverless on October 20, 2026, and in Elastic Stack 9.6 for Elastic Cloud Hosted and self-managed deployments. Already have alerts in Kibana? The new system runs alongside your existing Classic Alerting system, so you can adopt what you like at your own pace.</p>
<p>Kibana Classic Alerting continues to handle common scenarios well. Pick a rule type, fill in its parameters, and attach actions as you always have. Classic Alerting is as simple and reliable as ever. Universal Alerting goes further, enabling you to express any ES|QL query as an alerting rule, control flapping with alert and recovery delay, use custom recovery queries, shape alert data to fit your needs, and schedule actions that respond and group alerts across your rules.</p>
<h2 id="whenuniversalalertingfits">When Universal Alerting fits</h2>
<p>It's a good fit when one or more of these is true:</p>
<ol>
<li>Your alert scenarios go beyond what Classic Alerting can offer, e.g., custom severities, workflow-powered actions, alert data control, full control over recovery, and more.</li>
<li>You want the flexibility of building rules using simplified query builders and later "dropping down" to edit and expand the generated ES|QL.</li>
<li>Your alerts come from more than one system, and you want to triage and analyze them in one view.</li>
<li>Fine-grained notification control matters, e.g., who is notified and how often, cross-rule grouping and processing, and more.</li>
<li>Your team wants all the flexibility offered by <a href="https://www.elastic.co/docs/explore-analyze/discover">Discover</a>, <a href="https://www.elastic.co/docs/reference/query-languages/esql">ES|QL</a>, and <a href="https://www.elastic.co/docs/explore-analyze/workflows">Workflows</a>. Universal Alerting integrates perfectly with and depends on all of these foundational Elastic features.</li>
</ol>
<p>If a Classic rule type already covers your scenario, keep using it. We compare the two systems <a href="https://www.elastic.co/observability-labs/blog/kibana-universal-alerting#one-place-for-all-your-alerts-classic-universal-alerting-and-external">side by side</a> later in this post.</p>
<h2 id="howuniversalalertingworksfromesqlqueryrulestoalertdata">How Universal Alerting works: From ES|QL query rules to alert data</h2>
<p>A Universal Alerting rule runs an ES|QL query on a schedule. Each result is written as a rule event to a dedicated data stream, <code>.rule-events</code>. Four core ideas define the system:</p>
<ul>
<li>Rules are ES|QL queries, so there are no rule types to pick from. Each row the query returns, identified by its group, becomes a rule event, and the columns it returns are stored with the event. The query defines the shape of the alert.</li>
<li>Alerts are data. Rule events live in a regular data stream with a documented schema, whether they came from Kibana alerting rules or an external tool. They can be searched in Discover, charted on dashboards, and used as the input for other rules.</li>
<li>An alert follows one ongoing problem through its lifecycle (pending, active, recovering, inactive) and groups all of its underlying rule events as the alert's history. A rule doesn't trigger a new alert on every run, nor does it update an existing alert document. Rule events can be captured without triggering alerts. These are simply collected as data.</li>
<li>Action policies live outside rules. They match on alert data, group and throttle them, and send them to workflows. Changing who gets notified doesn't mean editing an action per rule.</li>
</ul>
<p>Each of these is a separate component, and each one offers flexibility and controls on its own.</p>
<h2 id="thesreworkflowauthorandshapeonerule">The SRE workflow: Author and shape one rule</h2>
<p>Say you run production hosts across several availability zones. The goal is to alert when a host stays hot, with the severity level and availability zone the on-call engineer needs. A brief spike shouldn't count. The host examples use metrics from the Elastic Agent System integration (<code>system.*</code> fields), and the pod example later uses the Kubernetes integration (<code>kubernetes.*</code> fields). Swap in your own fields as needed.</p>
<h3 id="authoroneesqlrulewrittenthewayyoualreadyquery">Author: One ES|QL rule, written the way you already query</h3>
<p>This rule computes average CPU per host, assigns a severity and returns only the hosts worth a look:</p>
<pre><code>/*
  Average CPU per host, with a severity and a minimum-sample guard.
*/
FROM metrics-*
| WHERE system.cpu.total.norm.pct IS NOT NULL
| STATS
    avg_cpu_pct = ROUND(AVG(system.cpu.total.norm.pct) * 100, 0),
    samples = COUNT(*)
  BY host.name, cloud.availability_zone
    /* A column named severity sets the alert's built-in severity field
       (info, low, medium, high or critical). Hosts below every threshold get
       no severity, and the WHERE below drops them. The other returned
       columns, such as avg_cpu_pct and the zone, are stored on the alert and
       available to action policies and dashboards later. */
| EVAL severity = CASE(avg_cpu_pct &gt; 95, "critical", avg_cpu_pct &gt; 90, "high", avg_cpu_pct &gt; 85, "medium")
| WHERE samples &gt;= 10 AND severity IS NOT NULL
    /* A minimum-sample guard skips hosts that just came up and have only
       reported a few data points. */
| KEEP host.name, cloud.availability_zone, avg_cpu_pct, severity, samples
</code></pre>
<h3 id="shapedecidehowthealertbehavesovertime">Shape: Decide how the alert behaves over time</h3>
<p>The query decides what matches. The rule definition decides what happens as matches come and go. Here's the same rule in YAML, split into a base query, a breach condition, and a recovery condition:</p>
<pre><code>kind: alert

metadata:
  name: host-cpu-saturation
  routing_tags: [infra]                 # links this rule to action policies (lever 5)

schedule:
  every: 1m
  lookback: 5m

query:
  base: |
    FROM metrics-*
    | WHERE system.cpu.total.norm.pct IS NOT NULL
    | STATS
        avg_cpu_pct = ROUND(AVG(system.cpu.total.norm.pct) * 100, 0),
        samples = COUNT(*)
      BY host.name, cloud.availability_zone
    | EVAL severity = CASE(avg_cpu_pct &gt; 95, "critical", avg_cpu_pct &gt; 90, "high", avg_cpu_pct &gt; 85, "medium")
  breach:
    segment: WHERE samples &gt;= 10 AND severity IS NOT NULL

recovery:
  strategy: condition
  segment: WHERE avg_cpu_pct &lt; 70       # recover well below the breach threshold

no_data:
  strategy: keep_last                   # a host that is rebooting keeps its last known status

state_transition:
  pending:
    count: 2                            # alert delay: the alert goes active on the third consecutive breach
  recovering:
    count: 2                            # recovery delay: the alert goes inactive on the third consecutive recovery

grouping:
  fields: [host.name, cloud.availability_zone]   # one alert per host
</code></pre>
<p>Recovery, state transitions, grouping and no-data handling are each tunable, and levers 1 to 4 below cover them. Levers 5 and 6 pick up once the alert is active: who gets notified, and how responders triage it.</p>
<h2 id="sixleversforfinetuningyouralerts">Six levers for fine-tuning your alerts</h2>
<p>Universal Alerting has a control at each stage of the pipeline, listed here from earliest to latest. Use only the ones a scenario calls for.</p>
<h3 id="1thequerydecideexactlywhatcounts">1. The query: Decide exactly what counts</h3>
<p>ES|QL has everything needed to define a condition precisely. <code>WHERE</code> sets the scope, a minimum-sample guard ignores hosts that just came up, <code>CASE</code> assigns severities, and <code>SORT</code> with <code>LIMIT</code> restricts alerts to the worst offenders.</p>
<pre><code>/*
  Alert on only the three hottest pods, not every pod above the line.
*/
FROM metrics-*
| WHERE kubernetes.pod.cpu.usage.node.pct IS NOT NULL
| STATS avg_cpu_pct = ROUND(AVG(kubernetes.pod.cpu.usage.node.pct) * 100, 1) BY kubernetes.pod.name
| SORT avg_cpu_pct DESC
| KEEP kubernetes.pod.name, avg_cpu_pct
| LIMIT 3
    /* LIMIT caps the number of alerts a single run can produce.
       Use it when "the worst few" is the question you care about. */
</code></pre>
<p><code>LIMIT</code> has one side effect to plan for. A pod that drops out of the top three disappears from the results. With <code>no_breach</code> recovery, that counts as recovered, and the alert can activate again if the pod climbs back. If that matters for your rule, pair <code>LIMIT</code> with a recovery delay (lever 3).</p>
<h3 id="2groupingchoosetheunitofanalert">2. Grouping: Choose the unit of an alert</h3>
<p><code>STATS ... BY</code> defines what a single alert is. Group by <code>host.name</code> for one alert per host, or by <code>service.name</code> for one per service. Pick the unit someone can act on: a grouping that's too wide hides problems, and one that's too narrow multiplies them. If a grouping field is missing from some documents, those rows fall into a single null group and share one alert, while the other groups keep their own alerts. Handle missing values explicitly in the query if that matters.</p>
<p>Alerting rules return at most 1,000 rows unless you set a <code>LIMIT</code>. If a single run could produce more groups than that, add an explicit <code>LIMIT</code>.</p>
<h3 id="3alertdelayandrecoverydelaydefinehowanalertstartsandends">3. Alert delay and recovery delay: Define how an alert starts and ends</h3>
<p>Thirty seconds over a threshold isn't always an incident. An instance of recovery isn't always indicative of a real recovery. This kind of flapping is one of the biggest sources of alert noise, and two delay configurations handle it without touching the query.</p>
<p>The alert delay (<code>state_transition.pending</code>) is the number of evaluations an alert spends in the pending state before it goes active. With <code>count: 2</code>, the alert goes active on the third consecutive breach, so a short spike stays pending and never becomes an alert.</p>
<p>The recovery delay (<code>state_transition.recovering</code>) does the same for the recovering state before the alert goes inactive. A signal that bounces around the threshold stays one alert. Each phase accepts a <code>count</code>, a <code>timeframe</code> or both, plus an <code>operator</code> (<code>and</code> or <code>or</code>) that says whether both must be met or just one.</p>
<p>Recovery is a separate setting, and every alert rule must set one (<code>no_breach</code> by default):</p>
<ul>
<li><code>no_breach</code> recovers an alert when its group stops appearing in the breach results.</li>
<li><code>condition</code> recovers it when a different recovery condition (as opposed to alert condition) on the same base query is met, as in the rule above.</li>
<li><code>query</code> recovers it when a separate ES|QL query returns the alert's group. Recovery is per group, so each alert recovers only when the query returns its group.</li>
<li><code>manual</code> never recovers on its own. The alert stays active until someone manually resolves it.</li>
</ul>
<p>A common pattern is to recover well below the breach threshold; for example, breach at 90% CPU and recover under 67%. That way, an alert doesn't close the moment the metric dips under the line. And because <code>query</code> recovery can run its own ES|QL query, it can check different values than the breach does. The example below requires both CPU and load average to be back to normal before the alert closes:</p>
<pre><code># A recovery query that looks at more than the breach query does.
recovery:
  strategy: query
  query: |
    FROM metrics-*
    | STATS
        avg_cpu_pct = ROUND(AVG(system.cpu.total.norm.pct) * 100, 0),
        load_avg_5m = AVG(system.load.5)
      BY host.name, cloud.availability_zone
    | WHERE avg_cpu_pct &lt; 67 AND load_avg_5m &lt; 4
    | KEEP host.name, cloud.availability_zone
</code></pre>
<h3 id="4nodatahandlingdecidewhatsilencemeans">4. No-data handling: Decide what silence means</h3>
<p>When a group with an active alert stops showing up in the breach results, there are two very different explanations. Either the problem cleared up or the data source stopped reporting, and the alert condition alone can't tell them apart. No-data handling lets you decide, per rule, what happens in that moment, so a broken metrics agent doesn't quietly recover a real alert.</p>
<p>Set it with <code>no_data.strategy</code>, or with the no-data setting in the rule builder:</p>
<p>| Option | <code>no_data.strategy</code> | What happens |
| --- | --- | --- |
| Keep last status | <code>keep_last</code> | Holds the last known state. An active alert stays active, and a recovering alert stays recovering. A <code>status:no_data</code> rule event is written to the data stream.|
| Recover | <code>resolve</code> | Closes the alert the first time no data is found, moving it straight to inactive without waiting for the recovery delay. A <code>status:no_data</code> rule event is written to the data stream.|
| Do nothing | <code>ignore</code> | Skips the no-data check. With <code>no_breach</code> recovery, a gap in data looks like a recovery. With <code>condition</code> or <code>query</code> recovery, no event is written and the alert stays active. |</p>
<p>The right choice depends on what silence means for the rule. For infrastructure CPU, use Keep last status: if the agent stops sending data, an active CPU breach stays active until data comes back. For a rule that triggers an alert when a job backlog grows too large, Recover makes sense. Once the queue is empty, the query has nothing to return and the alert closes.</p>
<h3 id="5actionpoliciesroutegroupandthrottle">5. Action policies: Route, group and throttle</h3>
<p>Rules don't carry their own actions. Instead, an action policy selects alerts, bundles them into a payload, controls how often they repeat and dispatches them to a workflow. One action policy can cover alerts from many rules, so changing who gets paged for a hundred rules takes a single edit.</p>
<p>Policy scope selects the alerts an action policy handles, in two parts that can be combined. Routing tags limit the action policy to alerts from rules that carry at least one of those tags. Under Advanced matching, a <a href="https://www.elastic.co/docs/explore-analyze/query-filter/languages/kql">KQL</a> expression filters on alert fields such as <code>severity</code>, <code>group_hash</code> or <code>data.*</code>. When both are set, an alert has to pass both.</p>
<p>Routing tags connect an action policy to a rule. Add them in the Routing tags field on the Actions step of the rule form, and select the same tags in the action policy form. From then on, the action policy applies to every rule that carries one of those tags, including rules created later. The rule form's Applied policies panel lists every action policy that matches the rule, so the connection is easy to verify. To target a single rule, give it a routing tag that no other rule uses. Attaching or detaching a rule is then a matter of editing its routing tags, and the action policy itself doesn't change.</p>
<p>Routing tags aren't the same as a rule's regular tags. Regular tags group and filter rules in the rule list. They have no effect on notifications, so adding a regular tag to a rule never attaches an action policy to it.</p>
<p>Two more settings shape the notifications. Notify per controls bundling: one notification per alert, one per group of alerts across rules that share a field value such as the availability zone, or a single digest of everything the action policy matches. Frequency limits repeats, for example to at most once every 30 minutes.</p>
<p>Here are two action policies for the CPU rule, written as the settings chosen in the UI. Both are tied to the rule through its <code>infra</code> routing tag:</p>
<pre><code># Page the on-call engineer only for critical hosts, one page per availability zone.
Name:           infra-oncall-pager
Policy scope:   Routing tags: infra
                Advanced matching (KQL): severity : "critical"
Notify per:     Group, by data.cloud.availability_zone
Frequency:      At most once every 30 minutes
Destination:    pagerduty-infra workflow

# Send the lower severities to the infrastructure channel, batched per zone, at most hourly.
Name:           infra-team-digest
Policy scope:   Routing tags: infra
                Advanced matching (KQL): severity : "high" or severity : "medium"
Notify per:     Group, by data.cloud.availability_zone
Frequency:      At most once every 1 hour
Destination:    slack-infra-team workflow
</code></pre>
<p>An alert can match several action policies, and each one applies its own grouping and frequency. If a whole zone runs hot, the pager gets one grouped page rather than one per host, and the team channel gets an hourly summary of the high and medium hosts.</p>
<p>Action policies also work across rules. Give your CPU, memory and disk rules the same routing tag, select that tag in an action policy and set Notify per to Group by <code>data.cloud.availability_zone</code>. A zone where all three rules fire then sends one page instead of three. Switch Notify per to Digest, and the action policy sends a single notification for everything it matches. To alert on the pattern itself, see <a href="https://www.elastic.co/observability-labs/blog/kibana-universal-alerting#rules-on-top-of-your-alerts">rules on top of your alerts</a>.
Expand commentComment on line R230Resolved
Each destination in these examples is a workflow that calls your connector, such as Slack or PagerDuty. To learn how to build and enable one, see the <a href="https://www.elastic.co/docs/explore-analyze/workflows">Workflows documentation</a>.</p>
<h3 id="6triagecontrolsandmaintenancewindowsquietthingsonpurpose">6. Triage controls and maintenance windows: Quiet things on purpose</h3>
<p>Once an alert is active, a responder can acknowledge it, snooze the series for a set period or resolve it. Suppressed alerts stay out of notification channels but remain in the data. Nothing is deleted, and everything stays auditable. During a planned rollout, for example, snoozing the series keeps it out of notification channels until the snooze expires. Maintenance windows do the same for scheduled work and apply to Universal Alerting and Classic rules alike.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltef955ad52b501fd7/6acf7a53bcf6e058b0e5640c/alerts-actions.png" alt="The Alerts page with an alert details flyout and the Take action menu showing acknowledge, snooze and resolve" /></p>
<h2 id="oneplaceforallyouralertsclassicuniversalandevenexternal">One place for all your alerts: Classic, Universal, and even external</h2>
<p>Few teams have a single source of alerts. Some come from Classic Alerting rules, some from Universal Alerting rules, and some from other monitoring tools. Our new alerts views are built so responders can work from a single queue.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltec51c43df4d2b9ba/6acf7a6a6d1916350f15a8fb/alerts-multisource.png" alt="The Alerts page with Classic, Universal and external alerts" /></p>
<h3 id="alertsfromothertoolsasdata">Alerts from other tools, as data</h3>
<p>Alerts from external monitoring tools land in the same data stream as the alerts Elastic generates, and are searched, routed and triaged the same way. A workflow writes each one with the <code>alerting.create_alert</code> step, and the <code>source</code> field records where it came from. For the on-call engineer, that's one place to check instead of a tab per tool. External alert ingestion is an experimental feature, so expect it to change and improve as we learn how it's used.</p>
<h3 id="universalvsclassicfeaturecomparison">Universal vs. Classic feature comparison</h3>
<p>The two systems share a goal but make different tradeoffs. Classic Alerting favors guided setup for known scenarios, while Universal Alerting favors flexibility and control.</p>
<p>| Feature | Classic | Universal |
| --- | :---: | :---: |
| Prebuilt rule types with guided parameters | ✓ | Partial, more to come |
| Rules written as ES|QL queries | ✓ | ✓ |
| Basic threshold rules | ✓ | ✓ |
| Anomaly detection and APM anomaly rules | ✓ | Coming soon |
| Machine learning job health and transform health rules | ✓ | Coming soon |
| SLO burn rate rules | ✓ | Coming soon |
| Synthetics monitor status rules | ✓ | Coming soon |
| Alert fields defined by the rule's query | | ✓ |
| Alert event historical data, per run |  | ✓ |
| Alert delay (consecutive breaches before an alert goes active) | ✓ | ✓ |
| Recovery delay (consecutive recoveries before an alert auto-recovers) | | ✓ |
| Custom recovery condition or recovery query | | ✓ |
| Actions attached to each rule, sent directly to connectors | ✓ | |
| Reusable action policies shared across many rules, with grouping and throttling | | ✓ |
| Import alerts from external tools (experimental) | | ✓ |
| Triage and analysis in the combined Alerts page | ✓ | ✓ |
| Maintenance windows | ✓ | ✓ |</p>
<p>Universal and Classic alerts appear together on the combined Alerts page, so responders triage everything in one view. Classic Alerting keeps running exactly as it does today. Maintenance windows, connectors, and workflows are shared across both systems.</p>
<p>New alerting capabilities are actively being built in Universal Alerting. Classic rules remain a good fit for the standard scenarios they were designed for, and you can adopt Universal Alerting at your own pace, one rule at a time.</p>
<h2 id="whatuniversalalertingdoesntdoyet">What Universal Alerting doesn't do (yet)</h2>
<p>Universal Alerting doesn't match Classic Alerting feature for feature yet. We'd rather you hear about the gaps now than discover them mid-incident, so here's what to know before moving a rule over.</p>
<ul>
<li>Not every Classic rule type has a Universal equivalent yet. Universal rules are ES|QL queries, written by hand or generated by the rule builder. Threshold-style rules, such as custom, metric, log and index threshold rules, map well to the threshold rule builder, and many other conditions can be written directly in ES|QL. Rule types that rely on results computed elsewhere in the stack, such as anomaly detection, don't have a Universal counterpart yet, so those rules stay on Classic for now. The comparison table above lists them, and the rule library on our roadmap will add ready-made starting points for common patterns.</li>
<li>SLO and synthetics rules need extra work. Their conditions can be written in ES|QL against the SLO and synthetics data, but the multi-window burn rate logic and per-monitor settings of the Classic rules have to be rebuilt by hand. Keep those rules on Classic unless you need the extra control.</li>
<li>Classic rules are not automatically migrated to the Universal system. They keep running unchanged, and moving one to Universal Alerting means rebuilding its condition as an ES|QL rule.</li>
<li>Universal Alerting actions depend on workflows. A Classic rule sends its actions straight to connectors. A Universal rule has no actions of its own: action policies hand alerts to workflows, which are the only destination type, and the workflow then calls Slack, PagerDuty or any other connector. Before a Universal rule can notify anyone, at least one workflow must exist and be enabled.</li>
<li>Workflows require an Enterprise license on Elastic Cloud Hosted and self-managed deployments. Without this license, Universal rules still run and alerts are still written, but action policies cannot deliver to any destination, so no notifications go out. Existing action policies can still be viewed, disabled, snoozed and deleted.</li>
<li>Universal alerts don't provide flapping state configuration. Alert and recovery delays and custom recovery (lever 3) are the way to keep a noisy alert from firing too often.</li>
<li>Field names may differ between the two alert types. A query on a Universal alert field such as <code>group_hash</code> won't match any Classic alerts. Our unified alerts table mostly hides this distinction, but use shared filters (status, severity, rule, tags) in your own combined queries, or query each system separately.</li>
</ul>
<p>Two delivery details apply to every action policy. Notifications are delivered at least once, so in rare cases, such as a restart between dispatch and recording, the same notification can arrive twice. Make workflows idempotent. Notifications are also dispatched on a short polling interval rather than instantly, so don't depend on sub-second paging latency.</p>
<h2 id="understandyouralertingwithesql">Understand your alerting with ES|QL</h2>
<p>An alert and its history are documents in a data stream, so the language that defines a rule can also answer questions about your alerting. From an alert, jump into Discover and pivot to that host's metrics and logs without a separate tool. Both examples below query the <code>.rule-events</code> data stream.</p>
<pre><code>/*
  The ten rules that activated the most alerts in the last 7 days.
  rule.id is the rule's saved object ID, not its name.
*/
FROM .rule-events
| STATS alerts = COUNT_DISTINCT(alert.id) BY rule.id
| SORT alerts DESC
| LIMIT 10
    /* Rules at the top are good candidates for tuning. Adjust a threshold,
       raise the alert delay or change the grouping, then re-run this. */
</code></pre>
<pre><code>/*
  Alert volume per day for one rule, to confirm a change had the effect you wanted.
  rule.id is the rule's saved object ID, not its name.
*/
FROM .rule-events
| WHERE rule.id == "&lt;your-rule-id&gt;" AND type == "alert"
| STATS alerts = COUNT_DISTINCT(alert.id) BY day = BUCKET(@timestamp, 1 day)
| SORT day ASC
</code></pre>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt41c659d5f8a763e7/6acf7a7f0179ead3d02e4711/discover-alerts.png" alt="Discover running an ES|QL query on .rule-events that counts distinct alerts per day" /></p>
<h2 id="rulesontopofyouralerts">Rules on top of your alerts</h2>
<p>Since alerts are data, rules can run over them too. A rule on alert data is an ordinary ES|QL rule that reads <code>.rule-events</code> instead of logs and metrics. That lets it catch a pattern no single alert shows, such as three or more rules firing in the same availability zone. Alerts from Universal rules and from external tools work the same way here, because both land in the same data stream with the same fields. The alerts such a rule produces get the same lifecycle, action policies and triage controls as any other alert.</p>
<p>Signal rules are the building blocks for this. A rule with <code>kind: signal</code> (labeled Events in the UI) records breach and recovery events as data, without triggering alerts or notifying anyone. Use signal rules for conditions that matter mostly in combination, like high CPU, low free disk and elevated I/O wait on the same host. Then write an alert rule that fires when several of those signals line up.</p>
<h2 id="whatwereworkingonnext">What we're working on next</h2>
<h3 id="tryupcomingfeaturesperspace">Try upcoming features per space</h3>
<p>New and upcoming Kibana Universal Alerting features will ship behind an experimental feature flag, so teams can try them before they're enabled for everyone. The flag is the <strong>Universal Alerting: Experimental Features</strong> setting (<code>alerting:v2:experimentalFeatures</code>). It's off by default and set per Kibana space, so you can turn it on in a test or staging space and leave production spaces as they are. Changing it requires a page reload.</p>
<p>The flag gates every upcoming Universal Alerting feature. Everything that ships at GA works without it.</p>
<h3 id="whatscoming">What's coming</h3>
<p>While we aren't committing to any specific dates, this is where we're focused after GA:</p>
<ul>
<li>Sequence rule builder GA: A guided way to build rules that detect events happening in a particular order, without writing the whole query by hand (already available via "experimental features").</li>
<li>More rule builders: Provide more guided ways to build rule queries. (SLO, synthetics, anomaly detection, etc.)</li>
<li>A rule library of ready-made rules to start from and adapt, so a new rule doesn't begin with a blank query.</li>
<li>Alert deletion, for alerts that are no longer needed.</li>
<li>External alert import, generally available, with simpler setup and management.</li>
<li>Bi-directional alert syncing with external systems, so an action taken in one system is reflected in the other.</li>
<li>Further "no data" features.</li>
</ul>
<p>If one of these matters more to you than the rest, tell us. It helps us decide what to build first.</p>
<h2 id="getstarted">Get started</h2>
<p>Universal Alerting is generally available on Elastic Cloud Serverless, and on Elastic Cloud Hosted and self-managed deployments starting with Elastic Stack 9.6. On Elastic Cloud Hosted and self-managed deployments, workflow-based action policies require an Enterprise license, which includes 10,000 workflow executions per month (see <a href="https://www.elastic.co/subscriptions">subscriptions</a>). On Serverless, Elastic manages the alerting system.</p>
<h3 id="giveyourteamaccess">Give your team access</h3>
<p>Access is controlled with Kibana feature privileges, set per role in <strong>Stack Management &gt; Roles</strong>. Universal Alerting adds four, listed under Universal Alerting in the role editor, each with its own <strong>All</strong> or <strong>Read</strong> level:</p>
<p>| Privilege | Read | All |
| --- | --- | --- |
| Rules | View rules | Create, edit and delete rules |
| Alerts | View alerts | Triage alerts |
| Action Policies | View action policies | Create and edit action policies |
| Execution history | View execution history | Same as Read |</p>
<p>Most responders need Alerts: All and Rules: Read. Rule authors need Rules: All, plus Action Policies: All if they also manage notifications. Notifications run through workflows, so anyone who builds or edits them also needs <strong>All</strong> for <strong>Analytics &gt; Workflows</strong>. See <a href="https://www.elastic.co/docs/explore-analyze/workflows/get-started/setup">Set up Workflows</a> for the full list of Workflows privileges.</p>
<p>Privileges control what a user can manage, not what data a rule can read. Each rule runs with the API key of the user who last saved it, and that key determines what data the rule can query. Save your rules as a user who can read the indices they query. For how rules, action policies and workflows are authorized, see the <a href="https://www.elastic.co/docs/explore-analyze/alerting/experimental-alerting-system/authorization">authorization documentation</a>.</p>
<h3 id="createyourfirstrule">Create your first rule</h3>
<p>Open the Universal rules tab, create your first rule and tell us what you find. If something comes up that we didn't cover here, we want to hear about it.</p>
<p>Don't have a deployment yet? Start an <a href="https://cloud.elastic.co/registration">Elastic Cloud free trial</a>.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/kibana-universal-alerting</link>
    <guid isPermaLink="false">kibana-universal-alerting</guid>
    <category><![CDATA[What's New]]></category>
    <category><![CDATA[Incident Management]]></category>
    <dc:creator><![CDATA[Tia Milosevic,Jason Rhodes]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt51b3aa37674303d4/6acf7a1ff87eb7bd53b443e6/header.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 20 Oct 2026 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>