Alertmanager Routing and Silences
Introduction¶
Prometheus decides what is wrong (rules fire alerts); Alertmanager decides who hears about it, grouped how, and how often. It dedupes, groups, routes, silences, and inhibits.
The routing tree¶
route:
receiver: default-slack
group_by: [alertname, cluster, service]
group_wait: 30s # wait to batch alerts that fire together
group_interval: 5m # how often to send an updated batch for a group
repeat_interval: 4h # re-send an unresolved alert after this long
routes:
- matchers: [severity="critical"]
receiver: pagerduty
group_wait: 0s
continue: true # ALSO evaluate later routes / send to Slack too
- matchers: [severity="critical"]
receiver: critical-slack
- matchers: [team="payments"]
receiver: payments-slack
routes:
- matchers: [severity="warning"]
receiver: payments-tickets
- matchers: [alertname=~"Watchdog|DeadMansSwitch"]
receiver: heartbeat # to a dead-man's-switch monitor
- An alert enters at the root and walks down; it matches a child route if its
labels match the
matchers. First matching child wins unlesscontinue: true(then it also continues to siblings). - If no child matches, the alert uses the parent's receiver.
- Child routes inherit
group_by, timings, etc. unless overridden.
Grouping¶
group_by: [alertname, cluster, service] — all alerts sharing those label values
form one group and produce one notification (with all the alerts listed).
So "50 pods down on cluster X" is one page, not 50.
group_by: [...]— group by these labels.group_by: ['...']with'...'(literal three dots) — don't group, one notification per alert. Rarely what you want.group_wait— after the first alert in a new group, wait this long for more before sending (batches a cascade).group_interval— minimum time between notifications for the same group when its contents change (a new alert joins).repeat_interval— re-notify about an unresolved group after this long. Too short = nag fatigue; 4h is a common floor for pages.
Inhibition¶
Suppress alerts that are consequences of a bigger alert:
inhibit_rules:
- source_matchers: [alertname="NodeDown"]
target_matchers: [alertname=~"NodeExporterDown|HighLatency|InstanceDown"]
equal: [instance] # only inhibit targets on the SAME instance
If NodeDown fires for instance=web-3, don't also page about
NodeExporterDown on web-3 — it's the same event.
Silences vs inhibition vs mute times¶
- Silence — a manual, time-bounded mute for alerts matching a label matcher.
"I'm doing maintenance on db-2 for 2 hours." Created in the UI or
amtool. - Inhibition — automatic, rule-based, "don't alert on the symptom while the cause is alerting."
- Mute time intervals — scheduled quiet periods on a route (
mute_time_intervals), e.g. "don't send ticket-severity alerts on weekends". Useactive_time_intervalsfor the inverse.
Dead man's switch¶
Add an always-firing alert (Watchdog) that routes to an external
"if this stops arriving, something is broken" monitor (Healthchecks.io,
PagerDuty heartbeat). If your whole monitoring stack dies, Prometheus stops
sending Watchdog, and the external monitor pages you.
Verification and troubleshooting¶
amtool check-config alertmanager.yml
amtool config routes test --config.file=alertmanager.yml \
severity=critical team=payments alertname=HighErrorRate # which receiver?
amtool alert query
amtool silence add alertname=HighLatency instance=web-3 --duration=2h --comment="INC-123"
amtool silence query
curl -s localhost:9093/api/v2/alerts | jq
- No notifications at all — Prometheus isn't sending to Alertmanager
(
--web.enable-lifecycle/alerting.alertmanagersconfig, check the Prometheus "Status → Runtime" andprometheus_notifications_*metrics), the receiver's integration is misconfigured (bad Slack webhook / PagerDuty key — Alertmanager logs the send error), or a silence/mute is active. - Alert routed to the wrong receiver —
amtool config routes testwith the alert's exact labels shows the path. A too-broadmatcherson an earlier child caught it. Order andcontinue:matter. - Getting paged for a symptom of a known outage — missing inhibition rule.
- Nag every 5 minutes —
repeat_intervaltoo low, orgroup_intervalfiring because the group keeps changing (flapping alerts join/leave). Fix the flapping alert (for:clause), raise the intervals. - Resolved notifications are noisy —
send_resolved: falseon the receiver if you don't want them. - Silence not working — the matcher doesn't match the alert's labels
(Alertmanager matches on the alert's label set, including ones added by
relabeling);
amtool alert queryshows the actual labels. Also silences are per-Alertmanager unless you run an HA cluster with gossip. - HA Alertmanager sending duplicates — the cluster gossip isn't working
(
--cluster.peerconfig, port 9094); each instance thinks it's alone.
Related tools and reading¶
- On-site: YAML ↔ JSON converter, Regex Tester.
- Related posts: SLO burn-rate alerts, Alert fatigue and how to fix it.
Stuck on something this site can't fix?Reach out to Prabath directly on LinkedIn.More in Monitoring
PromQL for Beginners
PromQL trips people up because counters must be wrapped in rate() and you can't average a percentile. Here's the mental model and the queries you'll actually write.
October 24, 2026The Prometheus Blackbox Exporter
The blackbox exporter probes your services the way a user would — is it up, is TLS valid, is DNS resolving. Here's the module config and the relabeling that makes one exporter probe many targets.
October 24, 2026SLO Burn-Rate Alerts
Alerting on 'error rate > 1%' is noisy. Alerting on 'we'll burn the whole month's error budget in a day' is actionable. Here's the burn-rate method and the queries.
October 24, 2026