ObservabilityLXVIII · Prometheus HAPrometheusHA
Alertmanager HA
What you'll learn
- Configure an Alertmanager gossip cluster with the cluster.listen-address and cluster.peer directives
- Choose the right cluster size for the production reliability target
- Recognise the most common Alertmanager HA failure: network partition causing duplicated alerts
- Apply cluster.replica-label so alerts from duplicate-scrape Prometheuses collapse to one notification
- Validate gossip membership and notification paths against a real cluster
Prerequisites
Verified against Prometheus 2.55.x · Alertmanager 0.28.x · node_exporter 1.8.x · blackbox_exporter 0.26.x · Grafana 11.x · Loki 3.x · Tempo current · OpenTelemetry Collector 0.110.x · Grafana Alloy current · Docker Engine 28.x · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL / Rocky / AlmaLinux 9.x · 2026-08-13
Two Alertmanagers. A network blip in the middle of a paging incident. One node loses contact with the other for 90 seconds. Both nodes think they are the only cluster member. Both nodes process the same batch of incoming alerts. Both nodes fire the same notifications to the same on-call channel. The on-call gets paged twice for the same condition.
Alertmanager HA is built on a gossip protocol. The protocol is eventually consistent: every node eventually agrees on the state of the cluster, but during partitions the two sides disagree. The disagreement is acceptable for some failure shapes (silences are eventually consistent, so a silence created on node A reaches node B after a few seconds) and dangerous for others (notifications are processed per node, so two nodes can fire two pages).
The discipline is to know which is which.
What it is
Alertmanager HA is the practice of running two or more Alertmanager instances that share the same alert pipeline. The cluster is gossip-based: nodes discover each other through a memberlist protocol (the same one HashiCorp Consul uses for its cluster membership). There is no leader election, no primary/secondary split, no shared state service. Every node has the same view of the alert pipeline within a few seconds of any change.
The contrast is single-instance Alertmanager. A single Alertmanager is a single point of failure. If the host dies, the on-call stops receiving pages until the process is restarted or replaced. The fix is to run two or three. The trade-off is that two or three introduce their own failure shapes (gossip partitions, duplicate notifications), and the discipline is to handle those deliberately.
Why a sysadmin cares
Alertmanager is the on-call’s last line of defence. If Alertmanager is down, the team does not know it is down. If Alertmanager is misconfigured, the team gets paged twice per incident and learns to ignore pages.
Two failure shapes appear repeatedly in production Alertmanager deployments:
- The single-node that died. The cluster was a single Alertmanager. The host OOMed during a paging storm. The on-call learned about the production outage from a user complaint rather than from an alert. The fix is to run at least two Alertmanagers.
- The partitioned cluster. Two Alertmanagers behind a load
balancer with a flaky link between them. The link flaps.
During the flap, both nodes process notifications
independently. Pages fire twice. The on-call learns to ignore
the second page and misses a real incident. The fix is to
know the partition behaviour and to apply
cluster.replica-labelso duplicate-scrape alerts collapse before notification.
How it works
The Alertmanager cluster gossip:
+------------+ gossip over UDP/TCP +------------+
| Alertmgr A | <---------------------------> | Alertmgr B |
| peer list: | | peer list: |
| A, B, C | | A, B, C |
+-----+------+ gossip over UDP/TCP +-----+------+
| <---------------------------> |
| |
+----------------+---------------------------+
|
v
+----------------+
| Alertmgr C |
| peer list: |
| A, B, C |
+-------+--------+
|
v
Silences / Notifications
deduplicated per alert
Every node runs the memberlist protocol. Every node periodically sends a ping to a peer; if the peer does not respond within a few seconds, the node is marked as failed and the failure is gossiped to the rest of the cluster. The membership view is eventually consistent across the cluster within a few seconds.
The notification path:
- Prometheus sends an alert to the Alertmanager endpoint (HA VIP, DNS round-robin, or a load balancer).
- The receiving node processes the alert. If the node is part of a cluster, it gossip-replicates the alert state to every peer.
- After the inhibition and silence phases, the node decides whether to notify. The decision is per node.
- The node fires the notification through the configured receiver (Slack, PagerDuty, Opsgenie, email).
The deduplication happens at the notification step (step 3).
The cluster does not elect a leader for notification; every
node decides for itself. If the cluster has replica-label
configured, the decision accounts for the replica label and two
alerts with the same name but different replica values are
treated as the same alert. If the flag is missing, they are
treated as distinct alerts and both nodes notify.
Under the hood
How to configure it
The minimum Alertmanager HA cluster is two nodes. The production-typical cluster is three.
# /etc/alertmanager/alertmanager.yml (on every node)
global:
resolve_timeout: 5m
route:
receiver: 'default'
group_by: ['alertname', 'cluster']
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
routes:
- matchers:
- severity = critical
receiver: 'pager'
group_wait: 10s
receivers:
- name: 'default'
slack_configs:
- api_url: 'https://hooks.slack.com/services/...'
channel: '#alerts'
- name: 'pager'
pagerduty_configs:
- service_key: '<key>'
cluster:
listen-address: "" # empty -> bind 0.0.0.0:9094
replica-label: replica # dedup across duplicate-scrape pairs
peers:
- am-a.prod.example.com:9094
- am-b.prod.example.com:9094
- am-c.prod.example.com:9094
inhibit_rules:
- source_matchers:
- severity = critical
target_matchers:
- severity = warning
equal: ['alertname', 'cluster']
The cluster.listen-address is empty so Alertmanager binds
all interfaces on port 9094. The cluster.peer list is the
same on every node; it tells every node who to contact for
membership. The cluster.replica-label is the dedup key.
Validate before starting:
# SEVERITY: READ-ONLY
amtool check-config /etc/alertmanager/alertmanager.yml
Start each node:
# SEVERITY: SERVICE-IMPACT
alertmanager \
--config.file=/etc/alertmanager/alertmanager.yml \
--storage.path=/var/lib/alertmanager \
--web.listen-address=0.0.0.0:9093 \
--cluster.listen-address=0.0.0.0:9094 \
--cluster.peer=am-a.prod.example.com:9094 \
--cluster.peer=am-b.prod.example.com:9094 \
--cluster.peer=am-c.prod.example.com:9094
On each node, the cluster.peer list is identical. The node
contacts the listed peers, learns the full peer list, and
gossips with everyone.
The Prometheus side needs to send alerts to the cluster, not to a single node. The standard pattern is a DNS A record with all three nodes, or a load balancer in front of the three nodes. The Prometheus configuration:
# /etc/prometheus/prometheus.yml
alerting:
alertmanagers:
- static_configs:
- targets: ['am-vip.prod.example.com:9093']
If the VIP or DNS is unhealthy, every Prometheus must fall back to a different target, otherwise the alert path is lost.
How to validate it
The first check is that every node sees every other node in its peer list:
# SEVERITY: READ-ONLY
curl -s http://am-a:9093/api/v1/status | jq '.cluster.status'
Expected output (illustrative):
{
"status": "ready",
"peerStatus": {
"am-a.prod.example.com:9094": { "name": "am-a", "address": "10.0.1.10:9094", "healthy": true },
"am-b.prod.example.com:9094": { "name": "am-b", "address": "10.0.1.11:9094", "healthy": true },
"am-c.prod.example.com:9094": { "name": "am-c", "address": "10.0.1.12:9094", "healthy": true }
}
}
Three healthy peers. If any peer is missing or unhealthy, the gossip has not converged yet, or the node cannot reach the peer.
The second check is that the alert pipeline is identical on every node:
# SEVERITY: READ-ONLY
amtool config show --alertmanager.url=http://am-a:9093 | head -30
amtool config show --alertmanager.url=http://am-b:9093 | head -30
amtool config show --alertmanager.url=http://am-c:9093 | head -30
The outputs should be byte-for-byte identical. A drift in the configuration between nodes causes one node to evaluate a different route tree from the others and fire different notifications.
The third check is that the replica-label is honoured.
Trigger an alert from a Prometheus with replica=a, then the
same alert from replica=b. Inspect the active alerts:
# SEVERITY: READ-ONLY
amtool alert query --alertmanager.url=http://am-a:9093
Expected output (illustrative): one alert, with
replica=a in the labels. The replica b copy of the alert
should not appear as a separate alert. If both appear, the
cluster.replica-label flag is missing or misconfigured.
The fourth check is that notifications are not doubled. Send the same alert from both Prometheuses and watch the receiver:
# SEVERITY: CONFIGURATION
amtool alert query --alertmanager.url=http://am-a:9093 \
| grep -c 'integrations'
The expected count is one notification per alert (one notification to Slack or PagerDuty), not two.
How it can fail
Six failure modes appear repeatedly in production Alertmanager clusters.
- Single-node deployment. The HA target was two nodes but
only one was started because the second was “still being
deployed.” Symptom: the single node processes every alert;
when it dies, no pages are sent. The fix is to verify every
node is running with
curl /api/v1/status. - Partitioned cluster firing duplicates. A three-node
cluster loses connectivity between two halves (one node
alone, two nodes together). Symptom: during the partition,
both halves process notifications independently, and pages
fire twice. The fix is
cluster.replica-labeland a tolerance for the brief window where dedup has not caught up. - The replica label is not set on Alertmanager. The
Prometheuses have
replica=aandreplica=bexternal labels. The Alertmanager cluster has noreplica-labelconfigured. Symptom: every alert fires twice. The fix is to addcluster.replica-label: replicato every node and to reload. - The cluster.peers list is empty or stale. A new node is
added with no
cluster.peerdirectives. Symptom: the new node cannot discover the existing cluster; it runs as a single-node cluster; alerts sent to it are not gossiped. The fix is to populatecluster.peerwith at least one reachable existing node. - DNS resolves the VIP to a dead node. The Prometheus alerting target is a DNS name that resolves to a single Alertmanager that has crashed. Symptom: no alerts reach any Alertmanager. The fix is to ensure the DNS returns multiple addresses (round-robin) or to use a load balancer that performs health checks.
- The gossip port is firewalled. The cluster
listen-addressis bound, but the firewall on the host blocks UDP/TCP 9094 between nodes. Symptom: nodes never see each other; each runs as a single-node cluster; alerts fire N times for an N-node cluster. The fix is to open the port in the host firewall and the network policy.
How to troubleshoot it
The diagnostic order when pages are doubled or not arriving:
- Confirm the cluster membership.
curl /api/v1/statuson each node; compare the peer lists. - Confirm the
replica-labelsetting.amtool config showon each node; check theclusterblock. - Confirm the Prometheus alerting target resolves to a
healthy node.
digornslookupon the VIP andcurlon each IP. - Confirm the gossip port is open between nodes.
nc -zvu am-b 9094andnc -zv am-b 9094. - Confirm the alertmanager configuration is identical across
nodes.
amtool config showdiff. - Confirm the notification configuration is identical and the receivers are reachable.
Security implications
Alertmanager exposes the web UI on port 9093 and the API on the same port. The API allows silencing, alert acknowledgement, and configuration inspection. Authentication is via basic auth or OAuth2 proxy. A misconfigured reverse proxy without auth exposes the silencer to anyone who can reach the port.
The gossip port (9094) does not require authentication, but the protocol is internal-cluster only. The port should be firewalled to allow traffic only from the cluster’s node IPs. Exposing 9094 to the public internet allows any host to join the cluster and inspect alert state.
The notification endpoints (Slack webhooks, PagerDuty keys, Opsgenie tokens) are credentials. The configuration file should be readable only by the Alertmanager user, and the secrets should be sourced from a secret store rather than committed to the repository.
Performance implications
Alertmanager is a low-throughput service compared to Prometheus. The alert evaluation runs every 15-30 seconds; each evaluation produces a small number of alert state changes; notifications are typically a few per hour. A single Alertmanager can handle the alert pipeline for tens of thousands of series.
The gossip overhead scales with the cluster size. Each node
gossips with every other node; the message rate is proportional
to O(N * frequency). For a 3-node cluster, the overhead is
small. For a 20-node cluster (unusual), the overhead becomes
significant and the cluster gossip can fall behind under load.
The notification path is synchronous per receiver. A slow receiver (a Slack webhook that times out, for example) blocks the notification pipeline until the timeout. The default notification timeout is configurable; the production value is typically 30 seconds per receiver.
Production guidance
- Run three Alertmanager nodes. Two is acceptable for small deployments; three gives tolerance for one node failure plus one network partition.
- Place the nodes in different availability zones or failure domains. A zone failure should not take down the whole cluster.
- Always set
cluster.replica-label: replicaon every node. The flag is the dedup point; without it, duplicate-scrape Prometheuses cause double pages. - Use a DNS round-robin or load balancer in front of the Alertmanager cluster for the Prometheus alerting target. The DNS must return multiple addresses and the addresses must be healthy.
- Open the gossip port (9094) between cluster nodes in the firewall and the network policy.
- Keep the Alertmanager configuration in a version-controlled repository and reload every node from the same source. Drift between nodes causes divergent notification paths.
- Monitor the cluster membership. Alert on
alertmanager_cluster_membersdropping below the expected count. - Monitor the notification path. Alert on
alertmanager_notifications_failed_totalrising.
Verification
You should now be able to answer:
- How does Alertmanager gossip propagate the alert state across the cluster, and how does it decide which node fires a notification?
- What is the right number of Alertmanager nodes for a production HA deployment, and why is two acceptable but three typical?
- Which flag tells Alertmanager to dedup alerts across duplicate-scrape Prometheuses, and on which config block does it live?
- What is the most common Alertmanager HA failure shape, and how does it surface?
- How does a partitioned cluster process notifications, and what limits the duplication?
Quiz
Knowledge check · 8 questions
Q1. Which mechanism does Alertmanager use to share cluster membership and alert state across nodes?
Q2. How many Alertmanager nodes are typical for a production HA cluster?
Q3. A two-node Alertmanager cluster is a valid HA deployment for small production environments.
Q4. A network partition splits a three-node Alertmanager cluster into one isolated node and two nodes that can still talk to each other. What happens to notifications during the partition?
Q5. Name the two Alertmanager config keys that configure the gossip cluster.
Q6. Which of these are valid Alertmanager HA failure modes?
Q7. The cluster.replica-label flag is set to replica on every Alertmanager node. A duplicate-scrape Prometheus pair fires the same alert with replica=a and replica=b. What does the receiver see?
Q8. A new Alertmanager node is added with no cluster.peer directives. What is the most likely symptom?
Passing score: 75%. Answers are checked in this browser.