Prometheus exporter that measures real end-to-end email delivery and roundtrip
1.8K
If you have ideas, suggestions, or constructive criticism, please share them in the discussion or open an issue:
Join the discussion
Open an issue
A Prometheus-compatible exporter that continuously verifies real end-to-end email delivery: send via SMTP and receive via IMAP. For every configured route a test mail is sent with a unique token in the subject; the exporter polls the inbox, measures round-trip latency, and exposes results as Prometheus metrics.
Copy environment and config templates
cp .env.example .env
cp config.example.yaml config.yaml
Start the service (from repo root)
docker compose up -d --build mail-e2e-exporter
Smoketest the endpoints
curl -s http://localhost:9782/health | jq .
curl -s http://localhost:9782/info | jq .
curl -s -u "$METRICS_USER:$METRICS_PASS" http://localhost:9782/metrics | head -n 30
Reload config on demand (if API_KEY is set)
curl -s -X POST -H "X-API-Key: $API_KEY" http://localhost:9782/reload | jq .
Note: docker-compose.yaml mounts ./config.yaml read-only to /app/config.yaml. File changes on the host are picked up automatically at the next background cycle, or immediately after /reload.
Gmail specifics: the IMAP search will try common Gmail labels (All Mail/Spam/Important in EN/DE variants) and prefers X-GM-RAW when available. You can also add imap.extra_folders for custom labels and adjust receive_timeout_seconds if needed.
Scrape /metrics from Prometheus. Examples:
Same Docker network
scrape_configs:
- job_name: 'mail-e2e-exporter'
static_configs:
- targets: ['mail-e2e-exporter:9782']
Via host port (local)
scrape_configs:
- job_name: 'mail-e2e-exporter-local'
static_configs:
- targets: ['localhost:9782']
With Basic Auth
scrape_configs:
- job_name: 'mail-e2e-exporter-secure'
static_configs:
- targets: ['mail-e2e-exporter:9782']
basic_auth:
username: '${METRICS_USER}'
password: '${METRICS_PASS}'
Reverse proxy: place an upstream to mail-e2e-exporter:9782 and protect /metrics; set metrics_path: /metrics in Prometheus if scraping via hostname.
Assuming metrics_prefix = "mail_e2e_exporter_" (default). All core series have labels route, from, to. The error counter also has label step in {send, receive, config}.
| Metric | Type | Description |
|---|---|---|
mail_e2e_exporter_send_success{from,to,route} | gauge | 1 if SMTP send succeeded, else 0. |
mail_e2e_exporter_receive_success{from,to,route} | gauge | 1 if IMAP receive succeeded, else 0. |
mail_e2e_exporter_roundtrip_seconds{from,to,route} | gauge | Time in seconds from send to receive. |
mail_e2e_exporter_last_send_timestamp{from,to,route} | gauge | Unix timestamp of the last send attempt. |
mail_e2e_exporter_last_receive_timestamp{from,to,route} | gauge | Unix timestamp of the last received test mail. |
mail_e2e_exporter_test_errors_total{from,to,route,step} | counter | Total errors, labeled by step (send, receive). |
mail_e2e_exporter_test_errors_created{from,to,route,step} | gauge | Timestamp when each error counter was created. |
mail_e2e_exporter_last_error_info{from,to,route} | gauge | Encoded hash of last error (0 = no error). |
mail_e2e_exporter_build_info{version,revision,build_date} | gauge | Version and build metadata (= 1). |
mail_e2e_exporter_config_delete_testmail_after_verify | gauge | 1 if test mails are deleted after success. |
mail_e2e_exporter_config_receive_timeout_seconds | gauge | Configured receive timeout. |
mail_e2e_exporter_config_receive_poll_seconds | gauge | Configured IMAP polling interval. |
mail_e2e_exporter_config_check_interval_seconds | gauge | Configured full check interval. |
mail_e2e_exporter_config_smtp_timeout_seconds | gauge | Configured SMTP timeout (effective). |
mail_e2e_exporter_receive_attempted{from,to,route} | gauge | 1 if the receive phase was attempted in the current cycle. |
mail_e2e_exporter_receive_skipped{from,to,route} | gauge | 1 if the receive phase was skipped due to send failure. |
mail_e2e_exporter_send_uncertain{from,to,route} | gauge | 1 if send failed due to timeout/disconnect after DATA; exporter may run a short IMAP probe. |
mail_e2e_exporter_send_rate_limited_total{from,to,route,code} | counter | Count of SMTP temporary failures (4xx like 451) during send; includes the server reply code. |
mail_e2e_exporter_test_info{from,to,route} | gauge | Always 1; maps configured routes for observability. |
Note: The actual metric names will use whatever exporter.metrics_prefix is set to at import time (can be "" for no prefix).
IMAP AUTHENTICATIONFAILED
Gmail message not found (timeout)
No tests configured
A ready-to-import dashboard JSON is provided under grafana/:
Import in Grafana:
docker network create monitoring_monitoring
Licensed under CC BY-NC 4.0. Free for private and internal (non-commercial) use. Attribution required: © 2025 Bjørn Ramos.
Content type
Image
Digest
sha256:1195a6efa…
Size
52.5 MB
Last updated
10 months ago
docker pull bjoernramos/mail-e2e-exporter