Semantic linter for Prometheus Alertmanager configs
Project description
amlint
Semantic linter for Prometheus Alertmanager configs.
amtool check-config validates syntax. amlint validates semantics:
will alerts actually reach a receiver, does the inhibition rule do anything,
are there unreachable routing branches? These are the bugs that burn teams —
config is valid, alerts silently vanish.
Why
Alertmanager configs are YAML routing trees with inhibition rules and receivers. The most painful mistakes are syntactically valid:
- route references a receiver that doesn't exist → alerts are dropped
- inhibition without
equal→ silences unrelated alerts, you think it's quiet, there's actually a fire - catch-all branch before specific ones → specific branches are unreachable
match_rethat doesn't compilegroup_bythat doesn't behave the way you think
amtool won't catch any of this. amlint will.
Install
pip install amlint
Or with Docker (no Python required):
docker run --rm -v $(pwd):/cfg ghcr.io/danikdanik2013/amlint check /cfg/alertmanager.yml
Usage
amlint check alertmanager.yml
amlint check prod.yml staging.yml # multiple files
cat alertmanager.yml | amlint check - # stdin
amlint check alertmanager.yml --strict # WARN also exits non-zero
amlint check alertmanager.yml --format json
amlint check alertmanager.yml --ignore empty-receiver,unused-receiver
amlint diff old.yml new.yml # show what changed
amlint init > alertmanager.yml # generate minimal valid config
amlint explain undefined-receiver # detailed explanation + examples
Project config — .amlint.yml or pyproject.toml [tool.amlint]:
# .amlint.yml
ignore:
- empty-receiver
strict: true
severity:
unused-receiver: error # upgrade info → error
# pyproject.toml
[tool.amlint]
ignore = ["empty-receiver"]
strict = true
Shell completions (bash/zsh):
pip install "amlint[completions]"
eval "$(register-python-argcomplete amlint)" # add to ~/.zshrc or ~/.bashrc
Example output:
ERROR Route references receiver 'pager-team' which is not defined in receivers. Alerts matched here will be dropped.
↳ route.routes[1] [undefined-receiver]
WARN Catch-all route (no matchers) with continue:false will intercept all alerts — 2 subsequent sibling(s) are unreachable.
↳ route.routes[0] [unreachable-route]
2 error · 3 warn · 1 info
Exit code 1 on ERROR — ready for CI. --strict makes WARN block too.
CI example
# .github/workflows/lint.yml
- name: Lint Alertmanager config
run: amlint check alertmanager.yml --strict
Checks
| code | level | what it catches |
|---|---|---|
undefined-receiver |
error | route references a receiver that doesn't exist |
bad-regex |
error | match_re pattern fails to compile |
no-root-route |
error | no root route defined |
duplicate-receiver |
error | receiver name defined more than once |
undefined-time-interval |
error | mute_time_intervals / active_time_intervals references unknown interval |
email-no-smarthost |
error | email_configs without smarthost and no global SMTP |
webhook-no-url |
error | webhook_configs without url or url_file |
pagerduty-no-routing-key |
error | pagerduty_configs without routing_key |
slack-no-api-url |
error | slack_configs without api_url and no global |
opsgenie-no-api-key |
error | opsgenie_configs without api_key and no global |
msteams-no-webhook-url |
error | msteams_configs without webhook_url |
inhibit-no-equal |
warn | inhibition without equal silences too broadly |
unreachable-route |
warn | catch-all hides subsequent sibling routes |
groupby-ellipsis |
warn | ... mixed with explicit labels in group_by |
repeat-before-group |
warn | repeat_interval shorter than group_interval |
circular-inhibition |
warn | two inhibition rules that silence each other |
wait-exceeds-interval |
warn | group_wait longer than group_interval |
empty-receiver |
warn/info | receiver has no integration configured — alerts will be dropped |
unused-receiver |
info | receiver defined but not used in any route |
inhibit-same-match |
info | source and target match the same label value |
useless-continue |
info | continue:true on the last sibling route has no effect |
deep-nesting |
info | route tree deeper than 5 levels |
Run amlint explain <code> for detailed description and examples of any check.
Tests
python3 -m pytest test_linter.py -v
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file amlint-0.1.7.tar.gz.
File metadata
- Download URL: amlint-0.1.7.tar.gz
- Upload date:
- Size: 17.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1c39e0ee492de20afa14ee3dbe4ccbb88407836fabd14dbfdeb72c37d87534fc
|
|
| MD5 |
adfe0d1bfed94fd7d81729266e5eed43
|
|
| BLAKE2b-256 |
9b98c55e8b01ad83c10429951aff712f07f7c1a17b60574dee90d67e45a865e1
|
Provenance
The following attestation bundles were made for amlint-0.1.7.tar.gz:
Publisher:
publish.yml on danikdanik2013/amlint
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
amlint-0.1.7.tar.gz -
Subject digest:
1c39e0ee492de20afa14ee3dbe4ccbb88407836fabd14dbfdeb72c37d87534fc - Sigstore transparency entry: 1923857463
- Sigstore integration time:
-
Permalink:
danikdanik2013/amlint@cf8a5f562d2956f54c2f67c96b5fe2177d0d1818 -
Branch / Tag:
refs/tags/v0.1.7 - Owner: https://github.com/danikdanik2013
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@cf8a5f562d2956f54c2f67c96b5fe2177d0d1818 -
Trigger Event:
push
-
Statement type:
File details
Details for the file amlint-0.1.7-py3-none-any.whl.
File metadata
- Download URL: amlint-0.1.7-py3-none-any.whl
- Upload date:
- Size: 17.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
30ef15a32d2eaf79901af8e2da0a117a8b95a8af037a58eb8f5e1b4edbeb4903
|
|
| MD5 |
789282b71fb93119d7a139323e2036e9
|
|
| BLAKE2b-256 |
ed7d0dd5000d07cb60faddd84ba65a6fe7e1a779a0490286202362c31d1f9a74
|
Provenance
The following attestation bundles were made for amlint-0.1.7-py3-none-any.whl:
Publisher:
publish.yml on danikdanik2013/amlint
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
amlint-0.1.7-py3-none-any.whl -
Subject digest:
30ef15a32d2eaf79901af8e2da0a117a8b95a8af037a58eb8f5e1b4edbeb4903 - Sigstore transparency entry: 1923857736
- Sigstore integration time:
-
Permalink:
danikdanik2013/amlint@cf8a5f562d2956f54c2f67c96b5fe2177d0d1818 -
Branch / Tag:
refs/tags/v0.1.7 - Owner: https://github.com/danikdanik2013
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@cf8a5f562d2956f54c2f67c96b5fe2177d0d1818 -
Trigger Event:
push
-
Statement type: