Continuous TLS certificate monitoring and alerting, powered by certinspect.
Project description
Scheduled checks · Expiry & revocation alerts · Fingerprint change detection · Deduplicated notifications · Console / email / Slack / webhook · Prometheus metrics
PyPI · Quick start · Configure · Alerts · Prometheus · Deployment · Issues
Continuous TLS certificate monitoring and alerting — the watch loop on top of certinspect.
certinspect tells you what a certificate looks like right now.
certminder runs it on a schedule, remembers what it saw last time, and
alerts you when a certificate is about to expire, gets revoked, changes
fingerprint, or becomes unreachable.
Why a separate tool
certminder never re-implements TLS or X.509 logic — that all lives in certinspect. certminder adds only what a monitor needs:
- a schedule (run once for cron, or loop as a daemon),
- state memory to detect changes between runs,
- deduplicated alerts (notify once per condition, recover once),
- pluggable notifiers (console, email, Slack, generic webhook),
- optional Prometheus metrics for the node_exporter textfile collector.
Install
pip install certminder # pulls in certinspect automatically
# or from source:
pip install -e '.[dev]'
Quick start
# inspect a single host ad hoc
certminder check example.com
# copy and edit the sample config, then:
certminder once -c certminder.yml # one cycle — ideal for cron
certminder run -c certminder.yml # run continuously as a daemon
Configure
Everything is driven by a YAML file (see
certminder.example.yml):
interval: 6h
state_file: ~/.certminder/state.json
defaults:
verify: true
days: 30
critical_days: 15
notifiers:
- type: console
- type: slack
webhook_url: "https://hooks.slack.com/services/XXX/YYY/ZZZ"
- type: email
host: smtp.example.com
port: 587
username: alerts@example.com
password: CHANGE_ME
from_addr: alerts@example.com
to: [ops@example.com]
targets:
- host: example.com
- host: api.example.com
port: 8443
- host: mail.example.com
starttls: smtp
What it alerts on
| Event | Severity | Trigger |
|---|---|---|
EXPIRING |
warning | within --days of expiry |
CRITICAL / EXPIRED |
critical | within critical_days, or already expired |
REVOKED |
critical | OCSP/CRL says revoked (needs verify) |
CHAIN_UNTRUSTED |
critical | chain fails to validate |
HOSTNAME_MISMATCH |
critical | cert does not match the hostname |
FINGERPRINT_CHANGED |
warning | fingerprint differs from last cycle |
UNREACHABLE |
critical | host/handshake failed |
RECOVERED |
info | a prior problem cleared |
Each condition alerts once; certminder remembers it and stays quiet until it changes, then sends a single recovery notice.
Exit codes (once)
0— no events this cycle1— at least one event was emitted2— configuration error
Add --json to once to print a machine-readable summary of the cycle (one
entry per target plus the events) to stdout, handy for piping:
certminder once -c certminder.yml --json | jq '.targets[] | {target, status, days_to_expire}'
Prometheus metrics
Set prometheus_file in the config to a path inside the node_exporter
textfile collector
directory. certminder rewrites it atomically at the end of every cycle:
certminder_certificate_expiry_days{target="example.com:443",host="example.com",port="443",status="VALID"} 42
certminder_certificate_valid{...} 1
certminder_target_up{...} 1
certminder_last_run_timestamp_seconds 1700000000
Deployment
Ready-to-use units live in deploy/ plus a Dockerfile:
- systemd timer —
certminder.service+certminder.timerrun one cycle on a schedule (cron-style, recommended). - systemd daemon —
certminder-daemon.serviceruns therunloop under supervision. - cron —
certminder.cronfor hosts without systemd timers. - Docker — multi-stage build; mount your
certminder.ymlat/etc/certminder/certminder.ymland a volume at/var/lib/certminder.
Docker
Build the image:
docker build -t certminder .
Run a single cycle (cron-style — config and state mounted from the host):
docker run --rm \
-v "$PWD/certminder.yml:/etc/certminder/certminder.yml:ro" \
-v certminder-state:/var/lib/certminder \
certminder once -c /etc/certminder/certminder.yml
Run continuously as a daemon (this is the default CMD):
docker run -d --name certminder \
--restart unless-stopped \
-v "$PWD/certminder.yml:/etc/certminder/certminder.yml:ro" \
-v certminder-state:/var/lib/certminder \
certminder
The named volume certminder-state persists state.json and the Prometheus
file across restarts — keep it so deduplication survives container recreation.
The console notifier prints to stdout; read it with docker logs -f certminder
(timestamps from Docker with -t, or set timestamp: true on the console
notifier). The container runs in UTC.
Docker Compose
services:
certminder:
build: . # or: image: certminder
container_name: certminder
restart: unless-stopped
command: run -c /etc/certminder/certminder.yml
volumes:
- ./certminder.yml:/etc/certminder/certminder.yml:ro
- certminder-state:/var/lib/certminder
logging: # cap the daemon's logs so they don't grow without bound
driver: json-file
options:
max-size: "10m"
max-file: "5"
volumes:
certminder-state:
docker compose up -d # build (if needed) and start the daemon
docker compose logs -f certminder
docker compose up -d --build # rebuild after upgrading certminder/certinspect
docker compose down # stop and remove
Development
ruff check . && ruff format --check .
pytest -q
Tests mock the certinspect subprocess, so the suite never touches the network.
License
MIT — see LICENSE.
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 certminder-0.3.0.tar.gz.
File metadata
- Download URL: certminder-0.3.0.tar.gz
- Upload date:
- Size: 24.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
79336bd563a15f5a591c38806cebad98c67b71b4adac8841f554d58cd1acea2a
|
|
| MD5 |
d2e9d36fc5ef5804d9af01cef424a932
|
|
| BLAKE2b-256 |
ce2ea0fba593202d0c67616f50d92667eae6ede53d8f1b2322060faf9b18b285
|
Provenance
The following attestation bundles were made for certminder-0.3.0.tar.gz:
Publisher:
publish.yml on mangrisano/certminder
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
certminder-0.3.0.tar.gz -
Subject digest:
79336bd563a15f5a591c38806cebad98c67b71b4adac8841f554d58cd1acea2a - Sigstore transparency entry: 1978621209
- Sigstore integration time:
-
Permalink:
mangrisano/certminder@eba80296497e9857825c61b14f5cba7be2fffa44 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/mangrisano
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@eba80296497e9857825c61b14f5cba7be2fffa44 -
Trigger Event:
push
-
Statement type:
File details
Details for the file certminder-0.3.0-py3-none-any.whl.
File metadata
- Download URL: certminder-0.3.0-py3-none-any.whl
- Upload date:
- Size: 22.1 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 |
fa7237c892dc451841603e76ed0c425fec596cf18d1f85238da404e8d70c7367
|
|
| MD5 |
5cf86d64b426fa65ee1ea7ac9bbb90bd
|
|
| BLAKE2b-256 |
30c4aafa04e77fabb0991b041248f263649734394e79570fb05074a4ad777805
|
Provenance
The following attestation bundles were made for certminder-0.3.0-py3-none-any.whl:
Publisher:
publish.yml on mangrisano/certminder
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
certminder-0.3.0-py3-none-any.whl -
Subject digest:
fa7237c892dc451841603e76ed0c425fec596cf18d1f85238da404e8d70c7367 - Sigstore transparency entry: 1978621305
- Sigstore integration time:
-
Permalink:
mangrisano/certminder@eba80296497e9857825c61b14f5cba7be2fffa44 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/mangrisano
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@eba80296497e9857825c61b14f5cba7be2fffa44 -
Trigger Event:
push
-
Statement type: