Configure Prometheus alerting with AlertManager notifications and webhook integration

Intermediate 25 min May 25, 2026 162 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Set up AlertManager to handle Prometheus alerts with email, Slack, and webhook notifications. Configure alerting rules, notification channels, and test the complete alerting pipeline.

Prerequisites

  • Prometheus server running
  • Basic understanding of YAML configuration
  • Email server access for notifications
  • Network access to webhook endpoints

What this solves

Prometheus collects metrics but doesn't send notifications when things go wrong. AlertManager bridges this gap by routing alerts to email, Slack, webhooks, and other channels. This tutorial sets up a complete alerting pipeline with notification routing and webhook integration.

Step-by-step installation

Update system packages

Start by updating your package manager to ensure you get the latest versions.

sudo apt update && sudo apt upgrade -y
sudo dnf update -y

Create AlertManager user

Create a dedicated system user to run AlertManager securely.

sudo useradd --no-create-home --shell /bin/false alertmanager

Download and install AlertManager

Download the latest AlertManager release from the official GitHub repository.

cd /tmp
wget https://github.com/prometheus/alertmanager/releases/download/v0.27.0/alertmanager-0.27.0.linux-amd64.tar.gz
tar xvf alertmanager-0.27.0.linux-amd64.tar.gz
sudo cp alertmanager-0.27.0.linux-amd64/alertmanager /usr/local/bin/
sudo cp alertmanager-0.27.0.linux-amd64/amtool /usr/local/bin/
sudo chown alertmanager:alertmanager /usr/local/bin/alertmanager
sudo chown alertmanager:alertmanager /usr/local/bin/amtool

Create AlertManager directories

Set up the directory structure for AlertManager configuration and data.

sudo mkdir -p /etc/alertmanager
sudo mkdir -p /var/lib/alertmanager
sudo chown alertmanager:alertmanager /etc/alertmanager
sudo chown alertmanager:alertmanager /var/lib/alertmanager

Configure AlertManager

Create the main AlertManager configuration file with routing rules and notification channels.

global:
  smtp_smarthost: 'localhost:587'
  smtp_from: 'alerts@example.com'
  smtp_auth_username: 'alerts@example.com'
  smtp_auth_password: 'your-email-password'
  
route:
  group_by: ['alertname', 'cluster', 'service']
  group_wait: 10s
  group_interval: 10s
  repeat_interval: 1h
  receiver: 'default-receiver'
  routes:
  - match:
      severity: critical
    receiver: 'critical-alerts'
  - match:
      alertname: InstanceDown
    receiver: 'instance-down-alerts'
  - match:
      service: database
    receiver: 'database-alerts'

receivers:
  • name: 'default-receiver'
email_configs: - to: 'admin@example.com' subject: 'Alert: {{ .GroupLabels.alertname }}' body: | {{ range .Alerts }} Alert: {{ .Annotations.summary }} Description: {{ .Annotations.description }} Labels: {{ range .Labels.SortedPairs }} {{ .Name }}: {{ .Value }} {{ end }} {{ end }}
  • name: 'critical-alerts'
email_configs: - to: 'oncall@example.com' subject: 'CRITICAL: {{ .GroupLabels.alertname }}' body: | Critical alert fired! {{ range .Alerts }} Alert: {{ .Annotations.summary }} Description: {{ .Annotations.description }} Severity: {{ .Labels.severity }} {{ end }} slack_configs: - api_url: 'https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK' channel: '#alerts' title: 'Critical Alert' text: | {{ range .Alerts }} {{ .Annotations.summary }} {{ .Annotations.description }} {{ end }}
  • name: 'instance-down-alerts'
webhook_configs: - url: 'http://localhost:8080/webhook/instance-down' send_resolved: true http_config: bearer_token: 'your-webhook-token' email_configs: - to: 'infrastructure@example.com' subject: 'Server Down: {{ .GroupLabels.instance }}'
  • name: 'database-alerts'
email_configs: - to: 'dba@example.com' subject: 'Database Alert: {{ .GroupLabels.alertname }}' webhook_configs: - url: 'http://localhost:8080/webhook/database' send_resolved: true

Set configuration permissions

Ensure AlertManager can read its configuration file with proper ownership.

sudo chown alertmanager:alertmanager /etc/alertmanager/alertmanager.yml
sudo chmod 644 /etc/alertmanager/alertmanager.yml

Create systemd service

Set up AlertManager as a systemd service for automatic startup and management.

[Unit]
Description=Prometheus AlertManager
Wants=network-online.target
After=network-online.target

[Service]
User=alertmanager
Group=alertmanager
Type=simple
WorkingDirectory=/var/lib/alertmanager
ExecStart=/usr/local/bin/alertmanager \
  --config.file=/etc/alertmanager/alertmanager.yml \
  --storage.path=/var/lib/alertmanager \
  --web.listen-address=0.0.0.0:9093 \
  --web.external-url=http://localhost:9093 \
  --cluster.listen-address=0.0.0.0:9094
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Start AlertManager service

Enable and start the AlertManager service to begin processing alerts.

sudo systemctl daemon-reload
sudo systemctl enable --now alertmanager
sudo systemctl status alertmanager

Configure Prometheus to use AlertManager

Update your Prometheus configuration to send alerts to AlertManager. If you don't have Prometheus installed yet, check our Prometheus and Grafana setup guide.

global:
  scrape_interval: 15s
  evaluation_interval: 15s

rule_files:
  - "alert_rules.yml"

alerting:
  alertmanagers:
    - static_configs:
        - targets:
          - localhost:9093

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']
  
  - job_name: 'node-exporter'
    static_configs:
      - targets: ['localhost:9100']
  
  - job_name: 'alertmanager'
    static_configs:
      - targets: ['localhost:9093']

Create Prometheus alerting rules

Define specific alert conditions that will trigger notifications through AlertManager.

groups:
  • name: system_alerts
rules: - alert: InstanceDown expr: up == 0 for: 5m labels: severity: critical annotations: summary: "Instance {{ $labels.instance }} is down" description: "{{ $labels.instance }} has been down for more than 5 minutes." - alert: HighCPUUsage expr: 100 - (avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80 for: 5m labels: severity: warning annotations: summary: "High CPU usage on {{ $labels.instance }}" description: "CPU usage is above 80% for more than 5 minutes." - alert: HighMemoryUsage expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 90 for: 5m labels: severity: critical annotations: summary: "High memory usage on {{ $labels.instance }}" description: "Memory usage is above 90% for more than 5 minutes." - alert: DiskSpaceLow expr: (1 - (node_filesystem_avail_bytes{fstype!="tmpfs"} / node_filesystem_size_bytes{fstype!="tmpfs"})) * 100 > 85 for: 5m labels: severity: warning annotations: summary: "Low disk space on {{ $labels.instance }}" description: "Disk usage is above 85% on {{ $labels.mountpoint }}."
  • name: database_alerts
rules: - alert: DatabaseConnectionsHigh expr: mysql_global_status_threads_connected / mysql_global_variables_max_connections * 100 > 80 for: 2m labels: severity: warning service: database annotations: summary: "High database connections on {{ $labels.instance }}" description: "Database connections are above 80% of maximum." - alert: DatabaseDown expr: mysql_up == 0 for: 1m labels: severity: critical service: database annotations: summary: "Database is down on {{ $labels.instance }}" description: "MySQL database is not responding."

Set alerting rules permissions

Ensure Prometheus can read the alerting rules file with proper ownership.

sudo chown prometheus:prometheus /etc/prometheus/alert_rules.yml
sudo chmod 644 /etc/prometheus/alert_rules.yml

Restart Prometheus to load alerts

Reload Prometheus configuration to activate the new alerting rules and AlertManager integration.

sudo systemctl restart prometheus
sudo systemctl status prometheus

Configure firewall for AlertManager

Open the necessary ports for AlertManager web interface and cluster communication.

sudo ufw allow 9093/tcp comment 'AlertManager web interface'
sudo ufw allow 9094/tcp comment 'AlertManager cluster'
sudo firewall-cmd --permanent --add-port=9093/tcp
sudo firewall-cmd --permanent --add-port=9094/tcp
sudo firewall-cmd --reload

Create webhook test endpoint

Set up a simple webhook endpoint to test AlertManager webhook integration.

#!/usr/bin/env python3
import json
import logging
from http.server import HTTPServer, BaseHTTPRequestHandler

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class WebhookHandler(BaseHTTPRequestHandler):
    def do_POST(self):
        content_length = int(self.headers['Content-Length'])
        post_data = self.rfile.read(content_length)
        
        try:
            alert_data = json.loads(post_data.decode('utf-8'))
            logger.info(f"Received webhook: {self.path}")
            logger.info(f"Alert data: {json.dumps(alert_data, indent=2)}")
            
            self.send_response(200)
            self.send_header('Content-type', 'application/json')
            self.end_headers()
            self.wfile.write(b'{"status": "received"}')
            
        except Exception as e:
            logger.error(f"Error processing webhook: {e}")
            self.send_response(400)
            self.end_headers()
    
    def log_message(self, format, *args):
        pass  # Suppress default logging

if __name__ == '__main__':
    server = HTTPServer(('localhost', 8080), WebhookHandler)
    logger.info("Webhook test server starting on http://localhost:8080")
    server.serve_forever()
chmod +x /tmp/webhook-test.py
python3 /tmp/webhook-test.py &

Test your alerting setup

Verify AlertManager is running

Check that AlertManager is accessible and processing configuration correctly.

curl http://localhost:9093/-/healthy
curl http://localhost:9093/api/v1/status

Check Prometheus alerts status

Verify that Prometheus is loading alerting rules and can reach AlertManager.

curl http://localhost:9090/api/v1/rules
curl http://localhost:9090/api/v1/alerts

Test alert firing manually

Create a test alert to verify the complete notification pipeline works.

# Send a test alert directly to AlertManager
curl -XPOST http://localhost:9093/api/v1/alerts -H "Content-Type: application/json" -d '[
  {
    "labels": {
      "alertname": "TestAlert",
      "severity": "warning",
      "instance": "test-server"
    },
    "annotations": {
      "summary": "This is a test alert",
      "description": "Testing AlertManager notification pipeline"
    },
    "startsAt": "'$(date -Iseconds)'",
    "generatorURL": "http://localhost:9090/"
  }
]'

View AlertManager web interface

Access the AlertManager web UI to see alerts and silences.

# Open in browser: http://your-server-ip:9093

Or check via curl

curl http://localhost:9093/api/v1/alerts

Advanced webhook integration

Create production webhook handler

Set up a more robust webhook endpoint for production use with authentication and logging.

#!/usr/bin/env python3
import json
import logging
import hashlib
import hmac
from datetime import datetime
from http.server import HTTPServer, BaseHTTPRequestHandler

Configure logging

logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler('/var/log/alertmanager-webhook.log'), logging.StreamHandler() ] ) logger = logging.getLogger(__name__) WEBHOOK_SECRET = 'your-webhook-secret-key' class ProductionWebhookHandler(BaseHTTPRequestHandler): def do_POST(self): # Verify authentication auth_header = self.headers.get('Authorization', '') if not auth_header.startswith('Bearer '): self.send_error(401, 'Missing or invalid authorization header') return token = auth_header.split(' ')[1] if token != 'your-webhook-token': self.send_error(403, 'Invalid token') return content_length = int(self.headers['Content-Length']) post_data = self.rfile.read(content_length) try: alert_data = json.loads(post_data.decode('utf-8')) # Log webhook details logger.info(f"Webhook received: {self.path}") logger.info(f"Number of alerts: {len(alert_data.get('alerts', []))}") # Process each alert for alert in alert_data.get('alerts', []): self.process_alert(alert) # Send success response self.send_response(200) self.send_header('Content-type', 'application/json') self.end_headers() response = { 'status': 'success', 'processed': len(alert_data.get('alerts', [])), 'timestamp': datetime.now().isoformat() } self.wfile.write(json.dumps(response).encode()) except json.JSONDecodeError: logger.error("Invalid JSON in webhook payload") self.send_error(400, 'Invalid JSON') except Exception as e: logger.error(f"Error processing webhook: {e}") self.send_error(500, 'Internal server error') def process_alert(self, alert): """Process individual alert - customize this for your needs""" alertname = alert.get('labels', {}).get('alertname') severity = alert.get('labels', {}).get('severity') status = alert.get('status') logger.info(f"Processing alert: {alertname} (severity: {severity}, status: {status})") # Example: Send to external system, update database, etc. if severity == 'critical': logger.warning(f"CRITICAL alert received: {alertname}") # Add your critical alert handling logic here if alertname == 'InstanceDown': instance = alert.get('labels', {}).get('instance') logger.error(f"Instance down alert: {instance}") # Add your instance down handling logic here def log_message(self, format, *args): pass # Use our custom logging instead if __name__ == '__main__': server = HTTPServer(('localhost', 8080), ProductionWebhookHandler) logger.info("Production webhook server starting on http://localhost:8080") try: server.serve_forever() except KeyboardInterrupt: logger.info("Webhook server stopped") server.shutdown()

Create webhook systemd service

Run the webhook handler as a systemd service for production reliability.

[Unit]
Description=AlertManager Webhook Handler
After=network.target

[Service]
Type=simple
User=alertmanager
Group=alertmanager
WorkingDirectory=/opt
ExecStart=/usr/bin/python3 /opt/alertmanager-webhook.py
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
sudo chown alertmanager:alertmanager /opt/alertmanager-webhook.py
sudo chmod +x /opt/alertmanager-webhook.py
sudo systemctl daemon-reload
sudo systemctl enable --now alertmanager-webhook
sudo systemctl status alertmanager-webhook

Verify your setup

# Check AlertManager status
sudo systemctl status alertmanager
curl http://localhost:9093/-/healthy

Check Prometheus can reach AlertManager

curl http://localhost:9090/api/v1/alertmanagers

View active alerts

curl http://localhost:9093/api/v1/alerts | jq

Check webhook endpoint

curl http://localhost:8080/health

Test alert routing with amtool

/usr/local/bin/amtool config routes test severity=critical alertname=TestAlert

View AlertManager logs

sudo journalctl -u alertmanager -f

Common issues

Symptom Cause Fix
AlertManager won't start Configuration syntax error /usr/local/bin/alertmanager --config.file=/etc/alertmanager/alertmanager.yml --config.check
No alerts received Prometheus can't reach AlertManager Check curl http://localhost:9090/api/v1/alertmanagers shows healthy connection
Email notifications not sent SMTP configuration incorrect Test SMTP settings with telnet localhost 587 and verify credentials
Webhook returns 404 URL path mismatch Check webhook URL matches your endpoint path exactly
Alerts stuck in pending Evaluation interval too long Reduce for duration in alert rules or check metric availability
Permission denied errors Wrong file ownership sudo chown -R alertmanager:alertmanager /etc/alertmanager /var/lib/alertmanager
Never use chmod 777. It gives every user on the system full access to your files. Instead, fix ownership with chown and use minimal permissions like 644 for config files.

Next steps

Running this in production?

Want this handled for you? Setting up AlertManager once is straightforward. Keeping it patched, monitored, backed up and tuned across environments is the harder part. See how we run infrastructure like this for European SaaS and e-commerce teams.

Automated install script

Run this to automate the entire setup

Need help?

Don't want to manage this yourself?

We handle managed devops services for businesses that depend on uptime. From initial setup to ongoing operations.