Configuration
End-to-end setup: plugin settings, what the plugin generates, and
what your alertmanager.yml needs around it.
Plugin settings
System Console → Plugins → Alertmanager exposes 5 settings. All are optional — the plugin runs with defaults on a fresh install.
WebhookHost
The host+port the plugin writes into rendered api_url: values.
Defaults to ServiceSettings.SiteURL when empty.
Set this when Alertmanager reaches Mattermost over a different network path than end users:
| Environment | Value |
|---|---|
| Docker compose | http://host.docker.internal:8065 |
| Kubernetes | http://mattermost.<namespace>.svc.cluster.local:8065 |
| Bare metal / shared host | Leave empty (SiteURL works) |
Format: <scheme>://<host>:<port> — no trailing slash, no path. The
runbook URLs embedded in alert post templates continue using SiteURL
since those are clicked by users in browsers, not consumed by
Alertmanager.
WebhookRotationDays
When non-zero, the background reconciler DMs sysadmins about
receivers whose webhook hasn't been rotated within this many days.
Default 0 = feature disabled.
Reminder-only — the plugin never auto-rotates. Per-receiver opt-in
mandatory via on flag on /alertmanager add. See
ROTATION.md for the full playbook.
Recommended values:
| Value | Use it when |
|---|---|
0 (default) |
No rotation discipline needed |
90 (quarterly) |
Baseline security hygiene — recommended |
180 (semi-annual) |
Lower-traffic channels |
365 (annual) |
Minimal viable rotation cadence |
Values below 30 produce noise unless your secret-rotation policy
actually rotates that frequently.
AlertManagerCABundle
PEM-encoded CA certificates used when the plugin queries
Alertmanager's REST API (/alertmanager status, alerts,
silences, expire_silence, route simulation). Concatenate
multiple certs in one block. Leave empty to trust only system CAs.
This setting only affects the plugin's outbound calls TO Alertmanager. The webhook URLs Alertmanager POSTs to are Mattermost-side and use whatever TLS Mattermost is configured with.
-----BEGIN CERTIFICATE-----
<your CA>
-----END CERTIFICATE-----
MetricsToken
When set, exposes Prometheus-format metrics at
/plugins/com.mattermost.alertmanager/metrics. Prometheus scrapes
the endpoint using this token in the Authorization: Bearer <token>
header. Leave empty to disable the endpoint entirely (returns 404
when unset).
Generate a random token:
openssl rand -hex 32
Configure Prometheus to scrape with the same token:
scrape_configs:
- job_name: mattermost-alertmanager-plugin
metrics_path: /plugins/com.mattermost.alertmanager/metrics
authorization:
type: Bearer
credentials: <your token>
static_configs:
- targets: ['<mattermost-host>:443']
AssembledYAMLTTLHours
The bot DMs sysadmins the assembled alertmanager-receivers.yml +
alertmanager-routes.yml files after every /alertmanager add and
/alertmanager rotate all --overdue. Those files contain
channel-bound webhook URLs (bearer tokens by URL).
This setting controls how many hours the DM'd files persist before
the auto-delete janitor removes them. Default 72. Set to 0 to
disable auto-delete (files persist forever — not recommended for
production).
Internal: AlertConfigsJSON
System Console → Plugins → Alertmanager → Advanced shows
AlertConfigsJSON — the JSON-serialized receiver list managed by
slash commands. Don't hand-edit unless you're recovering from a bug
or batch-importing from another tool. Schema:
[
{
"name": "high-cpu-usage--alert-slo-channel",
"team": "testing",
"channel": "alert-slo-channel",
"alertManagerURL": "http://alertmanager:9093",
"webhookID": "<MM webhook ID>",
"webhookHostOverride": "",
"lastRotatedAt": "2026-06-11T12:34:56Z",
"lastReminderAt": "0001-01-01T00:00:00Z",
"rotationRemindersEnabled": false
}
]
What the plugin generates
When you run /alertmanager add <team> <channel> <am-url> [set] [on]:
- Resolves the destination channel (auto-creates if missing).
- Mints an ephemeral PAT for the calling sysadmin, uses it to create one Mattermost incoming webhook per receiver in the chosen set, revokes the PAT.
- Stores each receiver in plugin config, named
<runbook-slug>--<team-slug>-<channel-slug>. StampsLastRotatedAt = now. Ifonwas passed, setsRotationRemindersEnabled = true. - Renders two YAML fragments and DMs them to the calling user:
alertmanager-receivers.yml— paste underreceivers:alertmanager-routes.yml— paste underroute.routes:
The plugin handles slack_configs blocks and the matching routes:
entries. Hook IDs are baked into the rendered api_url values; the
plugin remembers them so future /alertmanager rotate or
/alertmanager remove can find the right webhook to act on.
What your alertmanager.yml needs around the plugin's output
The plugin generates the receivers: and the route.routes: block.
You provide the surrounding structure:
# REQUIRED — global defaults
global:
resolve_timeout: 5m # how long after last notification before "resolved"
# REQUIRED — top-level routing
route:
receiver: default-fallback # catch-all for unrouted alerts (must exist below)
group_by: ['alertname', 'cluster']
group_wait: 30s # delay before first notification
group_interval: 5m # delay between notifications for same group
repeat_interval: 4h # repeat if still firing (4h+ for prod)
routes:
# <-- PASTE FROM alertmanager-routes.yml HERE -->
# REQUIRED — receivers section
receivers:
# REQUIRED — catch-all default referenced by `route.receiver` above.
- name: default-fallback
# <-- PASTE FROM alertmanager-receivers.yml HERE -->
# OPTIONAL — inhibit rules (suppress lower-severity when higher is firing)
inhibit_rules:
- source_matchers: [severity="critical"]
target_matchers: [severity="warning"]
equal: ['alertname', 'cluster']
Required fields
| Field | Why |
|---|---|
global block |
At least empty (global: {}) — AM parses for defaults |
route.receiver |
Top-level catch-all. Required even with sub-routes |
At least one entry in receivers: |
The receiver route.receiver references must exist |
Recommended for production
| Field | Why |
|---|---|
route.group_by |
Without grouping you get one notification per alert per evaluation cycle = spam |
route.group_wait |
Lets AM coalesce a burst of related alerts into one notification |
route.group_interval |
Throttles updates to a still-firing group |
route.repeat_interval |
Controls re-notification cadence (4h+ for production; shorter for dev/testing) |
Optional
| Field | When |
|---|---|
inhibit_rules |
Severity-based pairing (e.g., warning version should not fire when critical already firing) |
time_intervals / mute_time_intervals |
Scheduled silence windows (maintenance hours, weekends) |
templates |
External template files. Not needed — plugin bakes templates inline in each receiver's slack_configs |
What your Prometheus rules need
For the plugin-generated routes to dispatch alerts to the right receiver:
- alert: HighCPUUsage
expr: sum(rate(container_cpu_usage_seconds_total[5m])) by (namespace, pod) > 0.8
for: 10m
labels:
severity: critical
runbook: high-cpu-usage # ← matches a plugin receiver's base slug
annotations:
summary: "Pod CPU above 80% for 10 minutes"
description: "..."
The runbook label is what the plugin-generated routes match on.
The value must equal the receiver's base slug (e.g.,
high-cpu-usage, not high-cpu-usage--alert-slo-channel). Routes
auto-translate the base slug → suffixed receiver name.
Alerts without a runbook label fall through to route.receiver
(the default fallback).
Required Prometheus labels per runbook
Each shipped runbook documents the labels its Quick diagnostics
section expects in a "Required Prometheus labels" footer. Most
expect at least namespace and pod (for compute, application,
storage runbooks) plus the runbook-specific label like instance /
job / service. Security runbooks differ — several use apiserver /
Falco labels (e.g. k8s_ns_name, job) instead. See the
corresponding runbooks/*.md file, or run /alertmanager docs requirements for the full per-alert metric / label / tooling matrix.
If a label is missing on an incoming alert, the template falls back
to leaving the placeholder text in place — operators see
<namespace> rather than a substituted value. Validate with:
/alertmanager validate --simulate runbook=<slug> namespace=<ns> pod=<pod>
That walks AM's route tree against your label set without firing a real alert.
samples/prometheus-rules.yaml ships a complete rule set covering
all 30 runbooks with the correct label patterns. Use it as a
starting point.
Receiver naming convention
The plugin names every receiver <runbook-slug>--<team-slug>-<channel-slug>:
high-cpu-usage--sre-alert-slo-channel
high-cpu-usage--sre-oncall-critical
database-connectivity-loss--platform-dba-team-channel
The team slug is part of the name because channel names are unique
only per team — town-square exists in every team. Without it,
the same runbook delivered to same-named channels in different teams
would collide (Alertmanager requires globally-unique receiver names).
The -- after the runbook slug is the parse boundary — runbook
filenames never contain doubles, so the runbook slug is always
recoverable. The <team>-<channel> tail uses a single hyphen and is
identity/display only (team and channel are also stored as separate
fields); global uniqueness is enforced by the plugin rejecting
duplicate names on save, not by the separator.
Fan-out: one alert, multiple channels
To deliver the same runbook to multiple channels (e.g., CPU alerts
to both team channel AND oncall pager), run /alertmanager add
once per destination:
/alertmanager add testing alert-slo-channel http://alertmanager:9093 compute
/alertmanager add testing oncall-critical http://alertmanager:9093 compute
The plugin creates 6 receivers in each channel, all channel-suffixed:
high-cpu-usage--alert-slo-channelhigh-cpu-usage--oncall-critical- (and the rest of the compute set, in both)
/alertmanager export (or the next /alertmanager add DM)
produces a routes block like:
routes:
- matchers: [runbook="high-cpu-usage"]
receiver: high-cpu-usage--alert-slo-channel
continue: true # keep evaluating after match
- matchers: [runbook="high-cpu-usage"]
receiver: high-cpu-usage--oncall-critical
continue: true
continue: true on every plugin-generated route makes AM keep
evaluating routes after a match, so the alert hits both receivers
and both channels get the post. The plugin sets this
unconditionally — each runbook's matcher is unique, so continue
only changes behavior in the fan-out case, where it fixes the
otherwise-dead second route.
Channel auto-creation
The destination channel doesn't have to exist before you run
/alertmanager add. If missing, the plugin creates it as an open
channel under the named team. The bot user (@alertmanagerbot) is
the channel creator. The plugin doesn't add anyone else to the
channel — that's a Mattermost admin concern after creation.
Multiple Alertmanagers
The plugin supports multiple AM backends as independent registrations:
/alertmanager add testing alerts-east http://alertmanager.east.example.com:9093 compute
/alertmanager add testing alerts-west http://alertmanager.west.example.com:9093 compute
Each gets its own channel binding. /alertmanager status (when
invoked in either channel) queries the AM URL bound to that
channel's receivers. Independent error handling — failure on one
doesn't affect the other.
For one Mattermost serving multiple Alertmanagers reachable via different network paths (e.g., one MM serving K8s clusters in different VPCs), use the per-receiver host override:
/alertmanager add testing alerts-east http://am.east:9093 compute --webhook-host=http://mattermost.east.svc:8065
/alertmanager add testing alerts-west http://am.west:9093 compute --webhook-host=http://mattermost.west.svc:8065
The override takes precedence over the global WebhookHost setting
at YAML render time for those specific receivers.
HA Alertmanager (peers of the same logical instance)
Every peer fires its configured webhooks independently for the same alert group. The Mattermost incoming webhook receiver doesn't deduplicate by default, so identical posts may appear if multiple peers send simultaneously.
Mitigations from least to most invasive:
- Set
external_labelsidentical across peers so they group the same way at the AM side and one peer becomes the primary notifier - Front Mattermost with a deduplication proxy (out of scope for this plugin)
- Configure only one peer to send webhooks (loses redundancy)
Validating your configuration
Three commands cover the common verification paths:
# Cheap, read-only — checks AM reachable + receivers loaded in AM
/alertmanager validate
# Route simulation — walks AM's loaded route tree against a label set,
# reports which receivers would catch the alert. No alert fired.
/alertmanager validate --simulate runbook=high-cpu-usage severity=critical
# Diff your channel's assembled YAML against what AM has loaded,
# with schema validation via prometheus/alertmanager/config.Load
/alertmanager export --diff-against-loaded
The --diff-against-loaded mode catches undefined-receiver
references, malformed matchers, and route tree errors before the
operator pastes new YAML and reloads AM.
The admin inventory page at
/plugins/com.mattermost.alertmanager/admin/inventory provides the
same simulation as a GUI form (Mode / Type / Target / Channel /
Severity dropdowns) plus the org-wide inverse-drift view (receivers
in AM that the plugin doesn't track).