Alertmanager routing: Telegram notifications with inhibition rules
Published: 2026-03-20
Alertmanager receives alerts from Prometheus and routes them to receivers. Without a thoughtful route tree, you get paged for 200 alerts when a single node goes down. With inhibition rules and grouped routing, one alert fires and the rest are silenced automatically.
The configuration structure
yamlalertmanager:
config:
global:
resolve_timeout: 5m
route:
receiver: "null"
group_by: [alertname, cluster, namespace]
group_wait: 30s
group_interval: 5m
repeat_interval: 12h
routes:
- matchers: [severity=critical]
receiver: telegram-critical
repeat_interval: 4h
- matchers: [severity=warning]
receiver: telegram-warnings
repeat_interval: 24h
- matchers: [alertname=Watchdog]
receiver: "null"
inhibit_rules:
- source_matchers: [severity=critical]
target_matchers: [severity=warning]
equal: [alertname, namespace]
- source_matchers: [alertname=KubeNodeNotReady]
target_matchers: [alertname=KubePodNotRunning]
equal: [node]
receivers:
- name: "null"
- name: telegram-critical
telegram_configs:
- bot_token: "${TELEGRAM_BOT_TOKEN}"
chat_id: -100XXXXXXXXX
message: |
🔴 *{{ .GroupLabels.alertname }}*
{{ range .Alerts }}
*Namespace:* {{ .Labels.namespace }}
*Summary:* {{ .Annotations.summary }}
{{ end }}
- name: telegram-warnings
telegram_configs:
- bot_token: "${TELEGRAM_BOT_TOKEN}"
chat_id: -100XXXXXXXXX
message: |
⚠️ *{{ .GroupLabels.alertname }}*
{{ range .Alerts }}
*Namespace:* {{ .Labels.namespace }}
{{ .Annotations.description }}
{{ end }}
Telegram bot setup
1. Create a bot via @BotFather, save the token
2. Add the bot to a group channel
3. Get the chat_id: curl https://api.telegram.org/bot<TOKEN>/getUpdates
4. chat_id for groups is negative (-100XXXXXXXXX)
Store the token in a SealedSecret, reference it in Alertmanager config:
yamlalertmanager:
alertmanagerSpec:
configSecret: alertmanager-secret
Inhibition rules explained
Inhibition suppresses target alerts when a matching source alert is firing.
yaml- source_matchers: [alertname=KubeNodeNotReady]
target_matchers: [alertname=KubePodNotRunning]
equal: [node]
If KubeNodeNotReady fires for node=worker-1, all KubePodNotRunning alerts with node=worker-1 are suppressed. You already know the node is down — you don't need 20 pod alerts confirming it.
yaml- source_matchers: [severity=critical]
target_matchers: [severity=warning]
equal: [alertname, namespace]
If a critical fires, suppress warnings with the same name in the same namespace.
Grouping explained
group_wait: 30s— wait 30s after first alert before sending, collecting more alertsgroup_interval: 5m— minimum time between sending for the same group when new alerts arriverepeat_interval: 12h— re-notify every 12h if still firing. Set 4h for critical, 24h for warnings
Silence during maintenance
bashamtool --alertmanager.url=http://alertmanager.monitoring.svc:9093 \
silence add \
alertname=KubeNodeNotReady \
--duration=2h \
--comment="Scheduled maintenance on worker-1"
Or via the Alertmanager UI.
Testing the routing
bash# Test which receiver handles an alert
amtool --alertmanager.url=http://alertmanager.monitoring.svc:9093 \
config routes test \
--verify.receivers=telegram-critical \
alertname="KubeNodeNotReady" severity="critical"
# List all firing alerts and their state
amtool --alertmanager.url=http://alertmanager.monitoring.svc:9093 \
alert query
# Check inhibitions
amtool --alertmanager.url=http://alertmanager.monitoring.svc:9093 \
alert query --inhibited
Common pitfalls
All alerts going to "null": Missing a route or wrong matchers syntax. Use amtool config routes test to debug.
Telegram bot not delivering: The bot must be an admin in the group to send messages. Check the chat_id sign (group IDs are negative).
Alerts not resolving: resolve_timeout: 5m in global config. If Prometheus stops sending an alert (scrape fails), Alertmanager waits 5 minutes before marking it resolved.
Too many notifications for the same alert: Increase repeat_interval. During incident response you don't want to be re-notified every 4 hours.