{"id":164045,"date":"2026-03-24T03:38:04","date_gmt":"2026-03-24T00:38:04","guid":{"rendered":"https:\/\/computingforgeeks.com\/setup-prometheus-alertmanager-linux\/"},"modified":"2026-03-24T03:38:04","modified_gmt":"2026-03-24T00:38:04","slug":"setup-prometheus-alertmanager-linux","status":"publish","type":"post","link":"https:\/\/computingforgeeks.com\/setup-prometheus-alertmanager-linux\/","title":{"rendered":"Prometheus Alertmanager Setup &#8211; Slack, Email, PagerDuty"},"content":{"rendered":"\n<p>Alertmanager handles alert deduplication, grouping, silencing, and routing for Prometheus. It takes alerts from Prometheus and sends notifications to the right people through the right channels &#8211; email, Slack, PagerDuty, Microsoft Teams, and more. This guide covers a full Alertmanager setup with real-world routing configurations on both Ubuntu and Rocky Linux.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How Does Alertmanager Work?<\/h2>\n\n\n\n<p>Prometheus evaluates alert rules and sends firing alerts to Alertmanager. From there, Alertmanager does the heavy lifting:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Grouping<\/strong> &#8211; Combines related alerts into a single notification (e.g., all InstanceDown alerts in one message)<\/li>\n<li><strong>Deduplication<\/strong> &#8211; Prevents the same alert from being sent repeatedly<\/li>\n<li><strong>Routing<\/strong> &#8211; Directs alerts to different receivers based on labels (severity, team, service)<\/li>\n<li><strong>Inhibition<\/strong> &#8211; Suppresses less important alerts when a more critical one is firing<\/li>\n<li><strong>Silencing<\/strong> &#8211; Temporarily mutes specific alerts during maintenance windows<\/li>\n<\/ul>\n\n\n\n<p>For more details on the architecture, see the <a href=\"https:\/\/prometheus.io\/docs\/alerting\/latest\/alertmanager\/\" target=\"_blank\" rel=\"noopener\">official Alertmanager documentation<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Prerequisites<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A running Prometheus 3 instance &#8211; see <a href=\"https:\/\/computingforgeeks.com\/install-prometheus-ubuntu-debian\/\">Install Prometheus 3 on Ubuntu \/ Debian<\/a><\/li>\n<li>Ubuntu 24.04\/Debian 13 or Rocky Linux 10\/AlmaLinux 10<\/li>\n<li>SMTP credentials for email alerts (Gmail, Mailgun, or any SMTP server)<\/li>\n<li>Slack workspace with webhook URL (for Slack integration)<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Step 1: Install Alertmanager<\/h2>\n\n\n\n<p>Create a dedicated user for the Alertmanager service:<\/p>\n\n\n\n<p><strong>Ubuntu\/Debian:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo useradd --no-create-home --shell \/bin\/false alertmanager<\/code><\/pre>\n\n\n\n<p><strong>Rocky\/AlmaLinux:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo useradd --no-create-home --shell \/sbin\/nologin alertmanager<\/code><\/pre>\n\n\n\n<p>Create the configuration and data directories:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo mkdir -p \/etc\/alertmanager \/var\/lib\/alertmanager\nsudo chown alertmanager:alertmanager \/var\/lib\/alertmanager<\/code><\/pre>\n\n\n\n<p>Download and install the latest Alertmanager release:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>VER=$(curl -sI https:\/\/github.com\/prometheus\/alertmanager\/releases\/latest | grep -i ^location | grep -o v[0-9.]* | sed s\/^v\/\/)\necho \"Installing Alertmanager $VER\"\nwget https:\/\/github.com\/prometheus\/alertmanager\/releases\/download\/v${VER}\/alertmanager-${VER}.linux-amd64.tar.gz\ntar xvf alertmanager-${VER}.linux-amd64.tar.gz\nsudo cp alertmanager-${VER}.linux-amd64\/{alertmanager,amtool} \/usr\/local\/bin\/\nsudo chown alertmanager:alertmanager \/usr\/local\/bin\/{alertmanager,amtool}<\/code><\/pre>\n\n\n\n<p>At the time of writing, this installs Alertmanager <strong>0.31.1<\/strong>. Verify the installation:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>alertmanager --version<\/code><\/pre>\n\n\n\n<p>You should see the version and build info:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>alertmanager, version 0.31.1 (branch: HEAD, revision: e8d9fba4)\n  build user:       root@71dc7c3e3a06\n  build date:       20260228-14:18:37\n  go version:       go1.24.0\n  platform:         linux\/amd64<\/code><\/pre>\n\n\n\n<p>On <strong>Rocky\/AlmaLinux<\/strong>, set the SELinux context for the binaries:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo dnf install -y policycoreutils-python-utils\nsudo semanage fcontext -a -t bin_t \"\/usr\/local\/bin\/alertmanager\"\nsudo semanage fcontext -a -t bin_t \"\/usr\/local\/bin\/amtool\"\nsudo restorecon -v \/usr\/local\/bin\/{alertmanager,amtool}\nsudo semanage port -a -t http_port_t -p tcp 9093<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step 2: Configure Email Alerts (SMTP)<\/h2>\n\n\n\n<p>Email is the most common notification channel. The Alertmanager configuration uses YAML and supports multiple notification integrations in a single file.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo vi \/etc\/alertmanager\/alertmanager.yml<\/code><\/pre>\n\n\n\n<p>Start with a basic email configuration. Replace the SMTP settings with your actual mail server credentials:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>global:\n  resolve_timeout: 5m\n  smtp_smarthost: 'smtp.gmail.com:587'\n  smtp_from: 'alertmanager@example.com'\n  smtp_auth_username: 'alertmanager@example.com'\n  smtp_auth_password: 'your-app-password'\n  smtp_require_tls: true\n\ntemplates:\n  - '\/etc\/alertmanager\/templates\/*.tmpl'\n\nroute:\n  receiver: 'email-notifications'\n  group_by: ['alertname', 'instance']\n  group_wait: 30s\n  group_interval: 5m\n  repeat_interval: 4h\n\n  routes:\n    - match:\n        severity: critical\n      receiver: 'critical-email'\n      repeat_interval: 1h\n\n    - match:\n        severity: warning\n      receiver: 'email-notifications'\n      repeat_interval: 4h\n\nreceivers:\n  - name: 'email-notifications'\n    email_configs:\n      - to: 'team@example.com'\n        send_resolved: true\n\n  - name: 'critical-email'\n    email_configs:\n      - to: 'oncall@example.com'\n        send_resolved: true\n        headers:\n          Subject: '[CRITICAL] {{ .GroupLabels.alertname }} firing on {{ .GroupLabels.instance }}'<\/code><\/pre>\n\n\n\n<p>Key settings to understand:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>group_wait: 30s<\/code> &#8211; Wait 30 seconds for related alerts to arrive before sending the first notification<\/li>\n<li><code>group_interval: 5m<\/code> &#8211; Wait 5 minutes before sending updates about new alerts in the same group<\/li>\n<li><code>repeat_interval: 4h<\/code> &#8211; Don&#8217;t re-send the same alert more often than every 4 hours<\/li>\n<li><code>send_resolved: true<\/code> &#8211; Send a notification when an alert resolves (goes back to normal)<\/li>\n<\/ul>\n\n\n\n<p>For Gmail, you&#8217;ll need to generate an App Password in your Google Account settings. Regular passwords won&#8217;t work with SMTP if 2FA is enabled.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 3: Add Slack Integration<\/h2>\n\n\n\n<p>Slack is ideal for team-visible alerts. Create an incoming webhook in your Slack workspace under <strong>Apps > Incoming Webhooks<\/strong>, then add a Slack receiver to the configuration.<\/p>\n\n\n\n<p>Update the receivers section in <code>\/etc\/alertmanager\/alertmanager.yml<\/code> to include Slack:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>receivers:\n  - name: 'email-notifications'\n    email_configs:\n      - to: 'team@example.com'\n        send_resolved: true\n\n  - name: 'critical-email'\n    email_configs:\n      - to: 'oncall@example.com'\n        send_resolved: true\n\n  - name: 'slack-notifications'\n    slack_configs:\n      - api_url: 'https:\/\/hooks.slack.com\/services\/T00000000\/B00000000\/XXXXXXXXXXXXXXXXXXXXXXXX'\n        channel: '#alerts'\n        title: '{{ .GroupLabels.alertname }}'\n        text: >-\n          {{ range .Alerts }}\n          *Alert:* {{ .Annotations.summary }}\n          *Description:* {{ .Annotations.description }}\n          *Severity:* {{ .Labels.severity }}\n          {{ end }}\n        send_resolved: true\n        color: '{{ if eq .Status \"firing\" }}danger{{ else }}good{{ end }}'\n\n  - name: 'slack-critical'\n    slack_configs:\n      - api_url: 'https:\/\/hooks.slack.com\/services\/T00000000\/B00000000\/XXXXXXXXXXXXXXXXXXXXXXXX'\n        channel: '#critical-alerts'\n        title: 'CRITICAL: {{ .GroupLabels.alertname }}'\n        text: >-\n          {{ range .Alerts }}\n          *Alert:* {{ .Annotations.summary }}\n          *Instance:* {{ .Labels.instance }}\n          *Description:* {{ .Annotations.description }}\n          {{ end }}\n        send_resolved: true<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step 4: Add Microsoft Teams Webhook<\/h2>\n\n\n\n<p>Microsoft Teams supports incoming webhooks through Power Automate workflows (the old Office 365 Connector method is being deprecated). Create a workflow that accepts an HTTP POST and posts to a Teams channel, then use the webhook URL in Alertmanager.<\/p>\n\n\n\n<p>Add a Teams receiver using the webhook_configs integration:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>  - name: 'teams-notifications'\n    webhook_configs:\n      - url: 'https:\/\/prod-XX.westus.logic.azure.com:443\/workflows\/XXXXX\/triggers\/manual\/paths\/invoke?api-version=2016-06-01'\n        send_resolved: true\n        http_config:\n          follow_redirects: true<\/code><\/pre>\n\n\n\n<p>For richer formatting in Teams, you&#8217;ll need an intermediate webhook relay that transforms the Alertmanager JSON payload into an Adaptive Card format. The <a href=\"https:\/\/github.com\/prometheus-msteams\/prometheus-msteams\" target=\"_blank\" rel=\"noopener\">prometheus-msteams<\/a> project handles this conversion.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 5: Add PagerDuty Integration<\/h2>\n\n\n\n<p>PagerDuty is the standard for on-call alerting. Create a Prometheus integration in PagerDuty to get a routing key, then add this receiver:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>  - name: 'pagerduty-critical'\n    pagerduty_configs:\n      - routing_key: 'your-pagerduty-events-v2-routing-key'\n        severity: '{{ .GroupLabels.severity }}'\n        description: '{{ .GroupLabels.alertname }}: {{ .CommonAnnotations.summary }}'\n        details:\n          firing: '{{ .Alerts.Firing | len }}'\n          resolved: '{{ .Alerts.Resolved | len }}'\n          instance: '{{ .GroupLabels.instance }}'<\/code><\/pre>\n\n\n\n<p>PagerDuty uses the Events API v2 with routing keys (not service keys from v1). Get the routing key from <strong>Services > Service Directory > Your Service > Integrations > Events API v2<\/strong>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 6: Configure Severity-Based Route Tree<\/h2>\n\n\n\n<p>The real power of Alertmanager is its routing tree. Here&#8217;s a production-ready route configuration that sends critical alerts to PagerDuty and Slack, warnings to email, and informational alerts to Slack only:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>route:\n  receiver: 'email-notifications'\n  group_by: ['alertname', 'cluster', 'service']\n  group_wait: 30s\n  group_interval: 5m\n  repeat_interval: 4h\n\n  routes:\n    - match:\n        severity: critical\n      receiver: 'pagerduty-critical'\n      continue: true\n      repeat_interval: 30m\n\n    - match:\n        severity: critical\n      receiver: 'slack-critical'\n      repeat_interval: 1h\n\n    - match:\n        severity: warning\n      receiver: 'slack-notifications'\n      repeat_interval: 4h\n\n    - match_re:\n        alertname: '^(Watchdog|InfoInhibitor)$'\n      receiver: 'null'\n\nreceivers:\n  - name: 'null'\n\n  - name: 'email-notifications'\n    email_configs:\n      - to: 'team@example.com'\n        send_resolved: true\n\n  - name: 'pagerduty-critical'\n    pagerduty_configs:\n      - routing_key: 'your-pagerduty-routing-key'\n\n  - name: 'slack-critical'\n    slack_configs:\n      - api_url: 'https:\/\/hooks.slack.com\/services\/XXXXX'\n        channel: '#critical-alerts'\n        send_resolved: true\n\n  - name: 'slack-notifications'\n    slack_configs:\n      - api_url: 'https:\/\/hooks.slack.com\/services\/XXXXX'\n        channel: '#alerts'\n        send_resolved: true<\/code><\/pre>\n\n\n\n<p>Notice the <code>continue: true<\/code> on the first critical route. This tells Alertmanager to keep matching after this route, so critical alerts go to both PagerDuty AND Slack. Without <code>continue<\/code>, routing stops at the first match.<\/p>\n\n\n\n<p>The <code>null<\/code> receiver is a common pattern for dropping alerts you don&#8217;t want notifications for, like the Watchdog heartbeat alert from kube-prometheus-stack.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 7: Configure Inhibition Rules<\/h2>\n\n\n\n<p>Inhibition rules suppress less important alerts when a related critical alert is firing. This prevents alert floods &#8211; when a server is down, you don&#8217;t need separate notifications for every service on that server.<\/p>\n\n\n\n<p>Add this to your <code>alertmanager.yml<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>inhibit_rules:\n  - source_match:\n      severity: 'critical'\n    target_match:\n      severity: 'warning'\n    equal: ['alertname', 'instance']\n\n  - source_match:\n      alertname: 'InstanceDown'\n    target_match_re:\n      alertname: '.+'\n    equal: ['instance']<\/code><\/pre>\n\n\n\n<p>The first rule says: if a critical alert is firing, suppress any warning alerts with the same alertname and instance. The second rule suppresses all alerts for an instance when InstanceDown is firing &#8211; because if the machine is down, alerting on high CPU or low disk on that same machine is noise.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 8: Create Custom Notification Templates<\/h2>\n\n\n\n<p>The default Alertmanager notification format is functional but verbose. Custom templates let you control exactly what information appears in each notification and how it&#8217;s formatted.<\/p>\n\n\n\n<p>Create the templates directory and a custom email template:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo mkdir -p \/etc\/alertmanager\/templates\nsudo vi \/etc\/alertmanager\/templates\/email.tmpl<\/code><\/pre>\n\n\n\n<p>Add a clean, production-friendly email template:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{{ define \"email.custom.subject\" }}\n[{{ .Status | toUpper }}] {{ .GroupLabels.alertname }} ({{ .Alerts | len }} alerts)\n{{ end }}\n\n{{ define \"email.custom.html\" }}\n&lt;h2&gt;{{ .GroupLabels.alertname }}&lt;\/h2&gt;\n&lt;p&gt;Status: &lt;strong&gt;{{ .Status | toUpper }}&lt;\/strong&gt;&lt;\/p&gt;\n&lt;table border=\"1\" cellpadding=\"5\"&gt;\n  &lt;tr&gt;&lt;th&gt;Instance&lt;\/th&gt;&lt;th&gt;Severity&lt;\/th&gt;&lt;th&gt;Summary&lt;\/th&gt;&lt;\/tr&gt;\n  {{ range .Alerts }}\n  &lt;tr&gt;\n    &lt;td&gt;{{ .Labels.instance }}&lt;\/td&gt;\n    &lt;td&gt;{{ .Labels.severity }}&lt;\/td&gt;\n    &lt;td&gt;{{ .Annotations.summary }}&lt;\/td&gt;\n  &lt;\/tr&gt;\n  {{ end }}\n&lt;\/table&gt;\n{{ end }}<\/code><\/pre>\n\n\n\n<p>Reference the template in your email receiver config by adding <code>html<\/code> and <code>headers<\/code> fields:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>  - name: 'email-notifications'\n    email_configs:\n      - to: 'team@example.com'\n        send_resolved: true\n        html: '{{ template \"email.custom.html\" . }}'\n        headers:\n          Subject: '{{ template \"email.custom.subject\" . }}'<\/code><\/pre>\n\n\n\n<p>Templates use Go&#8217;s <code>text\/template<\/code> syntax. The most common variables are <code>.Status<\/code>, <code>.GroupLabels<\/code>, <code>.CommonLabels<\/code>, <code>.CommonAnnotations<\/code>, and <code>.Alerts<\/code> (which you iterate with <code>range<\/code>). Each alert inside <code>.Alerts<\/code> has <code>.Labels<\/code>, <code>.Annotations<\/code>, <code>.StartsAt<\/code>, and <code>.EndsAt<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 9: Create the Systemd Service<\/h2>\n\n\n\n<p>Create the systemd unit file for Alertmanager:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo vi \/etc\/systemd\/system\/alertmanager.service<\/code><\/pre>\n\n\n\n<p>Add the following service definition:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>[Unit]\nDescription=Prometheus Alertmanager\nDocumentation=https:\/\/prometheus.io\/docs\/alerting\/latest\/alertmanager\/\nWants=network-online.target\nAfter=network-online.target\n\n[Service]\nType=simple\nUser=alertmanager\nGroup=alertmanager\nExecStart=\/usr\/local\/bin\/alertmanager \\\n  --config.file=\/etc\/alertmanager\/alertmanager.yml \\\n  --storage.path=\/var\/lib\/alertmanager \\\n  --web.listen-address=0.0.0.0:9093 \\\n  --cluster.listen-address=\"\"\nRestart=always\nRestartSec=5\nSyslogIdentifier=alertmanager\n\n[Install]\nWantedBy=multi-user.target<\/code><\/pre>\n\n\n\n<p>The <code>--cluster.listen-address=\"\"<\/code> flag is critical for single-node deployments, especially on cloud VMs. Without it, Alertmanager tries to find a private IP for its gossip protocol and fails with <strong>&#8220;no private IP found&#8221;<\/strong> on many cloud providers. If you&#8217;re running a multi-node Alertmanager cluster, remove this flag and use <code>--cluster.peer<\/code> instead.<\/p>\n\n\n\n<p>Set ownership on the configuration file and start the service:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo chown alertmanager:alertmanager \/etc\/alertmanager\/alertmanager.yml\nsudo systemctl daemon-reload\nsudo systemctl enable --now alertmanager<\/code><\/pre>\n\n\n\n<p>Verify the service is running:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo systemctl status alertmanager<\/code><\/pre>\n\n\n\n<p>The service should show active (running):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\u25cf alertmanager.service - Prometheus Alertmanager\n     Loaded: loaded (\/etc\/systemd\/system\/alertmanager.service; enabled; preset: enabled)\n     Active: active (running) since Mon 2026-03-24 11:05:32 UTC; 4s ago\n       Docs: https:\/\/prometheus.io\/docs\/alerting\/latest\/alertmanager\/\n   Main PID: 4521 (alertmanager)\n      Tasks: 7 (limit: 4557)\n     Memory: 18.4M\n        CPU: 0.234s\n     CGroup: \/system.slice\/alertmanager.service\n             \u2514\u25004521 \/usr\/local\/bin\/alertmanager --config.file=\/etc\/alertmanager\/alertmanager.yml ...<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step 9: Open Firewall Ports<\/h2>\n\n\n\n<p>Alertmanager listens on port 9093. Open it in your firewall.<\/p>\n\n\n\n<p><strong>Ubuntu\/Debian (UFW):<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo ufw allow 9093\/tcp comment \"Alertmanager\"\nsudo ufw reload<\/code><\/pre>\n\n\n\n<p><strong>Rocky\/AlmaLinux (firewalld):<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo firewall-cmd --permanent --add-port=9093\/tcp\nsudo firewall-cmd --reload<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step 10: Access the Alertmanager Web UI<\/h2>\n\n\n\n<p>Open your browser and navigate to <code>http:\/\/your-server-ip:9093<\/code>. The Alertmanager UI shows active alerts, silences, and the current routing configuration:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1920\" height=\"941\" src=\"https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/03\/alertmanager-ui.png\" alt=\"Alertmanager web UI showing alert groups, active alerts, and silence management interface\" class=\"wp-image-164035\" title=\"\" srcset=\"https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/03\/alertmanager-ui.png 1920w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/03\/alertmanager-ui-300x147.png 300w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/03\/alertmanager-ui-1024x502.png 1024w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/03\/alertmanager-ui-768x376.png 768w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/03\/alertmanager-ui-1536x753.png 1536w\" sizes=\"auto, (max-width: 1920px) 100vw, 1920px\" \/><figcaption class=\"wp-element-caption\">Alertmanager web UI with alert groups and silence management<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">How to Manage Silences with amtool<\/h2>\n\n\n\n<p>Silences temporarily mute alerts during maintenance windows. The <code>amtool<\/code> CLI makes managing silences easy without touching the web UI.<\/p>\n\n\n\n<p>First, configure amtool to know where Alertmanager is running:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo mkdir -p \/etc\/amtool\necho \"alertmanager.url: http:\/\/localhost:9093\" | sudo tee \/etc\/amtool\/config.yml<\/code><\/pre>\n\n\n\n<p>Create a silence for a specific alert during a maintenance window:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>amtool silence add alertname=InstanceDown instance=\"10.0.1.20:9100\" --duration=2h --comment=\"Scheduled maintenance on web-02\"<\/code><\/pre>\n\n\n\n<p>The command returns a silence ID that you can use to manage it:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>4b5e8f2a-1c3d-4e5f-9a8b-7c6d5e4f3a2b<\/code><\/pre>\n\n\n\n<p>List all active silences:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>amtool silence query<\/code><\/pre>\n\n\n\n<p>You&#8217;ll see a table of all active silences with their matchers and expiry times:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ID                                    Matchers                                          Ends                  Created By  Comment\n4b5e8f2a-1c3d-4e5f-9a8b-7c6d5e4f3a2b  alertname=InstanceDown instance=10.0.1.20:9100    2026-03-24 13:05:32   admin       Scheduled maintenance on web-02<\/code><\/pre>\n\n\n\n<p>Expire a silence before its scheduled end time:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>amtool silence expire 4b5e8f2a-1c3d-4e5f-9a8b-7c6d5e4f3a2b<\/code><\/pre>\n\n\n\n<p>You can also query the current alert status:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>amtool alert query<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">How to Validate Your Configuration<\/h2>\n\n\n\n<p>Always validate the Alertmanager config before restarting the service. A bad config will prevent Alertmanager from starting, which means no alerts get delivered.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>amtool check-config \/etc\/alertmanager\/alertmanager.yml<\/code><\/pre>\n\n\n\n<p>A successful validation returns:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Checking '\/etc\/alertmanager\/alertmanager.yml'  SUCCESS\nFound:\n - global config\n - route\n - 1 inhibit rules\n - 5 receivers\n - 0 templates<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">How to Debug Alert Routing<\/h2>\n\n\n\n<p>When alerts aren&#8217;t reaching the expected receiver, use amtool to test which route an alert would match:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>amtool config routes test --config.file=\/etc\/alertmanager\/alertmanager.yml severity=critical alertname=InstanceDown<\/code><\/pre>\n\n\n\n<p>This shows the receiver that would handle an alert with those labels:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pagerduty-critical<\/code><\/pre>\n\n\n\n<p>You can also view the full route tree:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>amtool config routes show --config.file=\/etc\/alertmanager\/alertmanager.yml<\/code><\/pre>\n\n\n\n<p>This prints a visual representation of your entire routing hierarchy, making it easy to spot misconfigurations.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Connect Alertmanager to Prometheus<\/h2>\n\n\n\n<p>If you haven&#8217;t already configured Prometheus to send alerts to Alertmanager, add the alerting block to your <code>\/etc\/prometheus\/prometheus.yml<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>alerting:\n  alertmanagers:\n    - static_configs:\n        - targets:\n            - localhost:9093<\/code><\/pre>\n\n\n\n<p>Reload Prometheus after the change:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo systemctl reload prometheus<\/code><\/pre>\n\n\n\n<p>Verify the connection by checking the Prometheus UI under <strong>Status > Runtime &#038; Build Information<\/strong>. The Alertmanager should appear as a discovered endpoint.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How to Test Alert Notifications<\/h2>\n\n\n\n<p>Don&#8217;t wait for a real incident to find out your alerts are broken. Use amtool to send a test alert through the entire pipeline:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>amtool alert add test_alert severity=critical instance=\"test-server:9090\" --annotation=summary=\"This is a test alert\" --annotation=description=\"Testing the alerting pipeline\"<\/code><\/pre>\n\n\n\n<p>This creates a real alert in Alertmanager that will be routed through your configured receivers. Check your email, Slack, or PagerDuty to confirm delivery. The test alert will auto-resolve after Alertmanager&#8217;s <code>resolve_timeout<\/code> (5 minutes with the default config).<\/p>\n\n\n\n<p>You can also trigger test alerts from Prometheus by temporarily lowering the threshold on an existing rule. For example, change <code>HighCPUUsage<\/code> threshold from 85 to 1 temporarily &#8211; this will fire on any system with more than 1% CPU usage, which is essentially every running system. Remember to revert the change after testing.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">High Availability with Alertmanager Clustering<\/h2>\n\n\n\n<p>For production environments where a single Alertmanager is a single point of failure, run multiple instances in a cluster. Alertmanager uses a gossip protocol to synchronize silences and notification state across instances. Each instance needs the <code>--cluster.peer<\/code> flag pointing to the other instances:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ExecStart=\/usr\/local\/bin\/alertmanager \\\n  --config.file=\/etc\/alertmanager\/alertmanager.yml \\\n  --storage.path=\/var\/lib\/alertmanager \\\n  --web.listen-address=0.0.0.0:9093 \\\n  --cluster.listen-address=0.0.0.0:9094 \\\n  --cluster.peer=10.0.1.11:9094 \\\n  --cluster.peer=10.0.1.12:9094<\/code><\/pre>\n\n\n\n<p>In the Prometheus configuration, list all Alertmanager instances so that if one goes down, alerts still reach the others:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>alerting:\n  alertmanagers:\n    - static_configs:\n        - targets:\n            - 10.0.1.10:9093\n            - 10.0.1.11:9093\n            - 10.0.1.12:9093<\/code><\/pre>\n\n\n\n<p>The cluster automatically deduplicates notifications, so you won&#8217;t get triple alerts even though all three instances receive the same firing alert from Prometheus. Port 9094 needs to be open between the Alertmanager nodes for gossip communication.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Set Up Alerting in Grafana<\/h2>\n\n\n\n<p>Grafana can also use Alertmanager as a notification target. In Grafana, go to <strong>Alerting > Contact points<\/strong> and add a new contact point with type &#8220;Alertmanager&#8221;. Set the URL to <code>http:\/\/localhost:9093<\/code>. This lets Grafana-managed alerts use the same routing and grouping logic as Prometheus alerts.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1920\" height=\"941\" src=\"https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/03\/grafana-alerting.png\" alt=\"Grafana alerting configuration showing Alertmanager as a contact point with connection status\" class=\"wp-image-164040\" title=\"\" srcset=\"https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/03\/grafana-alerting.png 1920w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/03\/grafana-alerting-300x147.png 300w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/03\/grafana-alerting-1024x502.png 1024w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/03\/grafana-alerting-768x376.png 768w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/03\/grafana-alerting-1536x753.png 1536w\" sizes=\"auto, (max-width: 1920px) 100vw, 1920px\" \/><figcaption class=\"wp-element-caption\">Grafana alerting with Alertmanager as the notification backend<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Troubleshooting Common Issues<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Alertmanager fails with &#8220;no private IP found&#8221;<\/h3>\n\n\n\n<p>This is the most common issue on cloud VMs. Alertmanager&#8217;s cluster discovery tries to find a private RFC 1918 IP address for gossip communication. On cloud instances with only public interfaces or NAT-ed networking, it fails.<\/p>\n\n\n\n<p>The fix for single-node deployments is to disable clustering entirely:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>--cluster.listen-address=\"\"<\/code><\/pre>\n\n\n\n<p>Add this to your systemd service ExecStart line. For multi-node clusters, explicitly set the bind address:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>--cluster.listen-address=\"10.0.1.10:9094\"<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Emails not being delivered<\/h3>\n\n\n\n<p>Check the Alertmanager logs for SMTP errors:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo journalctl -u alertmanager -n 50 --no-pager | grep -i smtp<\/code><\/pre>\n\n\n\n<p>Common issues: wrong port (use 587 for TLS, 465 for SSL), authentication failure (Gmail needs App Password, not regular password), or firewall blocking outbound SMTP. Test SMTP connectivity from the server:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl -v telnet:\/\/smtp.gmail.com:587<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Alerts are firing but no notifications arrive<\/h3>\n\n\n\n<p>First verify alerts are reaching Alertmanager:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>amtool alert query<\/code><\/pre>\n\n\n\n<p>If alerts appear there but notifications aren&#8217;t sent, the issue is in the route configuration. Test your routing:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>amtool config routes test --config.file=\/etc\/alertmanager\/alertmanager.yml severity=warning alertname=HighCPUUsage<\/code><\/pre>\n\n\n\n<p>If the test shows the wrong receiver, adjust your route matchers. Also check if the alert might be inhibited by an active inhibition rule or silenced by an active silence.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Slack messages not formatting correctly<\/h3>\n\n\n\n<p>Slack notification text uses Go template syntax. Common mistakes include missing <code>range<\/code> blocks for iterating over alerts and incorrect template variable names. Test your templates with a simple config first, then add complexity. The Alertmanager logs will show template rendering errors if the syntax is wrong.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Alertmanager restarts lose silence state<\/h3>\n\n\n\n<p>Alertmanager stores silence data in its <code>--storage.path<\/code> directory. If you&#8217;re running Alertmanager with a temporary storage path or the directory gets wiped, silences will be lost on restart. Make sure <code>\/var\/lib\/alertmanager<\/code> is persistent and backed up. The nflog (notification log) that tracks which notifications were already sent is also stored there &#8211; without it, Alertmanager may re-send alerts that were already delivered.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Alerts are grouping incorrectly<\/h3>\n\n\n\n<p>The <code>group_by<\/code> setting determines which alerts are bundled into a single notification. If you group by <code>['alertname']<\/code> only, all InstanceDown alerts for different servers will be sent as one message. If you want separate notifications per server, add <code>'instance'<\/code> to the group_by list. Be careful not to group by too many labels &#8211; this can lead to one notification per alert, which defeats the purpose of grouping and causes alert flood during outages.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Frequently Asked Questions<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Can I run Alertmanager on a different server than Prometheus?<\/h3>\n\n\n\n<p>Yes. Update the Prometheus <code>alertmanagers<\/code> target to point to the remote server IP and port. Make sure port 9093 is open in the firewall between the two servers. Running Alertmanager separately is common in larger deployments where you want the alerting system to survive even if the Prometheus server has issues.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How do I add a new notification channel without downtime?<\/h3>\n\n\n\n<p>Edit the Alertmanager config file and reload the service. Alertmanager supports hot reloading &#8211; either send SIGHUP to the process or POST to the <code>\/-\/reload<\/code> endpoint if <code>--web.enable-lifecycle<\/code> is enabled. Validate the config first with <code>amtool check-config<\/code> to avoid breaking a running instance.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>Alertmanager is the critical link between Prometheus detection and human action. With the routing, inhibition, and multi-channel configuration covered here, you have a production-ready alerting pipeline that routes critical issues to PagerDuty, sends warnings to Slack, and uses email as the catch-all. Fine-tune the <code>group_wait<\/code>, <code>group_interval<\/code>, and <code>repeat_interval<\/code> values based on your team&#8217;s response patterns &#8211; the defaults here are a solid starting point that prevent alert fatigue without missing real incidents.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Alertmanager handles alert deduplication, grouping, silencing, and routing for Prometheus. It takes alerts from Prometheus and sends notifications to the right people through the right channels &#8211; email, Slack, PagerDuty, Microsoft Teams, and more. This guide covers a full Alertmanager setup with real-world routing configurations on both Ubuntu and Rocky Linux. How Does Alertmanager Work? &#8230; <a title=\"Prometheus Alertmanager Setup &#8211; Slack, Email, PagerDuty\" class=\"read-more\" href=\"https:\/\/computingforgeeks.com\/setup-prometheus-alertmanager-linux\/\" aria-label=\"Read more about Prometheus Alertmanager Setup &#8211; Slack, Email, PagerDuty\">Read more<\/a><\/p>\n","protected":false},"author":3,"featured_media":164046,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[299,50,165],"tags":[],"class_list":["post-164045","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-how-to","category-linux-tutorials","category-monitoring"],"_links":{"self":[{"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/posts\/164045","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/comments?post=164045"}],"version-history":[{"count":0,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/posts\/164045\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/media\/164046"}],"wp:attachment":[{"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/media?parent=164045"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/categories?post=164045"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/tags?post=164045"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}