★ Enterprise Edition — not included in the Community Edition. Compare editions
Monitoring Stack Deployment¶
How the monitoring stack — Prometheus, Grafana, and Alertmanager — is deployed alongside your Mailyte email server.
Overview¶
There is no separate monitoring compose file. Prometheus, Grafana, Alertmanager, and the database exporters are services inside the main docker-compose.yml and come up with the rest of the stack.
graph LR
subgraph Scrape targets
RS[rspamd :11334]
API[api + every worker<br/>:its own /metrics]
ME[mysql-exporter :9104]
RE[redis-exporter :9121]
end
subgraph Monitoring services
PR[Prometheus :9090]
GR[Grafana :3000]
AM[Alertmanager :9093]
end
RS --> PR
API --> PR
ME --> PR
RE --> PR
PR --> GR
PR --> AM The images are pinned in the compose file:
| Service | Image | Host port (base file) |
|---|---|---|
prometheus | prom/prometheus:v2.51.0 | 9090 |
grafana | grafana/grafana:10.4.0 | 3000 |
alertmanager | prom/alertmanager:v0.27.0 | 9093 |
mysql-exporter | prom/mysqld-exporter:v0.15.1 | 9104 |
redis-exporter | oliver006/redis_exporter:v1.58.0 | 9121 |
In production (docker-compose.prod.yml), all five are rebound to 127.0.0.1; Grafana additionally gets a Traefik router at https://grafana.<DOMAIN>.
Configuration Layout¶
All configuration is bind-mounted from the repository's monitoring/ directory — edit there, then restart the service:
monitoring/
├── prometheus/
│ ├── prometheus.yml # Scrape configs (15s interval) + alerting wiring
│ └── rules/
│ ├── mail_alerts.yml # Mail-flow alert rules
│ └── backup_alerts.yml # Backup freshness alert rules
├── grafana/
│ ├── provisioning/
│ │ ├── datasources/prometheus.yml
│ │ └── dashboards/dashboards.yml
│ └── dashboards/
│ ├── mail_overview.json
│ └── security_dashboard.json
└── alertmanager/
└── alertmanager.yml
Prometheus scrapes every worker service on its container port (api:8080, webhooks:8081, monitoring:8085, ...), plus rspamd's built-in /metrics on 11334 and the two exporters. Time-series data lives in the prometheus_data named volume with 30-day retention.
Credentials¶
Grafana's admin login comes from .env:
GRAFANA_ADMIN_PASSWORD is one of the eight secrets the stack validates fail-closed at startup — sign-up is disabled (GF_USERS_ALLOW_SIGN_UP=false).
The MySQL exporter authenticates as the application user (DB_USER/DB_PASSWORD) and runs with --no-collect.slave_status because no replica exists in this stack.
Starting and Reloading¶
The monitoring services start with the stack — there is nothing extra to run:
After editing configuration:
# Prometheus supports live reload (--web.enable-lifecycle is on)
curl -X POST http://localhost:9090/-/reload
# Grafana and Alertmanager need a restart
docker compose restart grafana alertmanager
Post-Deploy Verification¶
# 1. Check Prometheus is up and scraping
curl -s http://localhost:9090/-/healthy
curl -s http://localhost:9090/api/v1/targets | python3 -c "
import json, sys
data = json.load(sys.stdin)
for t in data['data']['activeTargets']:
print(f\" [{t['health']}] {t['labels']['job']}\")
"
# 2. Check Grafana is accessible
curl -s http://localhost:3000/api/health
# 3. Check Alertmanager
curl -s http://localhost:9093/-/healthy
# 4. Verify a metric exists
curl -s 'http://localhost:9090/api/v1/query?query=up' | python3 -m json.tool
Two targets are expected to be down
prometheus.yml still defines postfix and dovecot scrape jobs pointing at postfix-exporter:9154 / dovecot-exporter:9166 — exporters that are not part of the compose stack. Those two targets showing down is a known configuration leftover, not a mail problem; Postfix delivery data reaches the platform through the log_ingestor service instead.
Accessing Remotely¶
In production these services are bound to 127.0.0.1. To reach them:
Option 1: Traefik (Grafana only, built in)¶
docker-compose.prod.yml already routes https://grafana.<DOMAIN> to Grafana, with a certificate issued by cert_manager (grafana is in the default TRAEFIK_ADMIN_SUBDOMAINS). Just publish the DNS record.
Option 2: SSH Tunnel (Prometheus / Alertmanager)¶
# From your local machine
ssh -L 3000:localhost:3000 -L 9090:localhost:9090 -L 9093:localhost:9093 user@your-server
# Now open http://localhost:9090 in your browser
Option 3: VPN¶
If your team uses a VPN terminating on the server, the loopback-bound ports are reachable through it.
Do not add your own nginx on the host for this — Traefik already owns ports 80/443, and only one process can bind them.
Beyond Prometheus: the monitoring service¶
Do not confuse the Prometheus stack with the monitoring worker service (port 8085). That service is Mailyte's own health monitor: it checks the other containers and restarts unhealthy ones through the scoped docker-proxy — the auto-healing described in Monitoring > Auto-healing. Both run by default.
Resource Usage¶
Production memory limits from docker-compose.prod.yml:
| Component | Limit |
|---|---|
| Prometheus | 1 GB |
| Grafana | 512 MB |
| Alertmanager | 256 MB |
| mysql-exporter / redis-exporter | 128 MB each |
Budget roughly 2 GB for the whole monitoring tier at moderate scrape volume.