Prometheus y Grafana son la combinación habitual para monitorizar un broker MQTT. Prometheus recoge métricas por HTTP y evalúa las reglas de alerta. Grafana dibuja los dashboards. Los brokers exponen sus métricas de dos maneras: algunos tienen un endpoint Prometheus nativo (EMQX, VerneMQ, HiveMQ mediante una extensión), mientras que otros, como Mosquitto, solo publican topics $SYS y necesitan un exporter que los traduzca.
Esta guía conecta las tres fuentes de datos que probablemente necesitarás: un exporter para Mosquitto, el endpoint nativo de EMQX y una sonda del lado del cliente que funciona con cualquier broker, incluidos los gestionados. Después añade dashboards y alertas.
De dónde salen las métricas
| Broker | Cómo obtiene Prometheus las métricas |
|---|---|
| Mosquitto | mosquitto-exporter se suscribe a $SYS/# y sirve /metrics (puerto 9234 por defecto) |
| EMQX 5 | Endpoint integrado /api/v5/prometheus/stats en el puerto del dashboard/API (18083), o envío a un Pushgateway |
| HiveMQ | HiveMQ Prometheus Monitoring Extension, sirve /metrics (puerto 9399 por defecto) |
| VerneMQ | /metrics integrado en su listener HTTP (puerto 8888 por defecto) |
| Broker gestionado | Métricas del proveedor si las ofrece, más tu propia sonda del lado del cliente |
Las métricas del broker te dicen qué está haciendo el broker: conexiones, throughput, colas y descartes. Una sonda del lado del cliente te dice lo que viven tus dispositivos: ¿pueden conectarse y cuánto tarda un mensaje? Necesitas ambas. Si solo puedes tener una, elige la sonda, porque es la que detecta fallos de DNS, TLS, balanceador y autenticación.
Si $SYS es nuevo para ti, lee primero monitorizar un broker con topics $SYS. El exporter de abajo es una capa fina encima.
Un stack local con Docker Compose
Este stack ejecuta Mosquitto, el exporter, Prometheus y Grafana juntos. Es un buen laboratorio para construir dashboards antes de apuntarlos a producción.
services:
mosquitto:
image: eclipse-mosquitto:2
ports: ["1883:1883"]
volumes:
- ./mosquitto.conf:/mosquitto/config/mosquitto.conf:ro
mosquitto-exporter:
image: sapcc/mosquitto-exporter
command: ["--endpoint", "tcp://mosquitto:1883"]
ports: ["9234:9234"]
depends_on: [mosquitto]
prometheus:
image: prom/prometheus
ports: ["9090:9090"]
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- ./alerts.yml:/etc/prometheus/alerts.yml:ro
grafana:
image: grafana/grafana
ports: ["3000:3000"]
volumes:
- ./grafana-datasource.yml:/etc/grafana/provisioning/datasources/prometheus.yml:rolistener 1883
allow_anonymous true
sys_interval 10apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
url: http://prometheus:9090
isDefault: trueallow_anonymous true solo es aceptable en un portátil. En un broker real, crea un usuario de monitorización que pueda leer $SYS/# y nada más, y pásaselo al exporter con --user y --pass. El generador de mosquitto.conf puede crear los bloques de listener y autenticación por ti. Para un laboratorio más completo con más servicios, consulta el stack MQTT con Docker Compose.
El exporter convierte cada topic $SYS numérico en una métrica cuyo nombre sigue la ruta del topic; por ejemplo, $SYS/broker/clients/connected pasa a ser broker_clients_connected. Abre http://localhost:9234/metrics para ver los nombres exactos que genera tu versión antes de escribir consultas.
Un exporter de sondeo para cualquier broker
Este exporter en Python (paho-mqtt 2.x y prometheus_client) se conecta por TLS cada 15 segundos, se suscribe a un topic único, publica un mensaje y espera a que vuelva. Exporta si ha funcionado y cuánto ha tardado cada paso:
import os, ssl, threading, time, uuid
import paho.mqtt.client as mqtt
from prometheus_client import Gauge, Histogram, start_http_server
HOST, PORT, INTERVAL = "broker.connectmq.com", 8883, 15
success = Gauge("mqtt_probe_success", "1 if the last connect/publish/receive cycle worked")
connect_s = Gauge("mqtt_probe_connect_seconds", "TCP + TLS + CONNACK time of the last probe")
roundtrip = Histogram("mqtt_probe_roundtrip_seconds", "Subscribe, publish and receive time",
buckets=(0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5))
def probe() -> bool:
topic = f"testmqtt/probe/{uuid.uuid4().hex[:8]}"
connected, received = threading.Event(), threading.Event()
c = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
client_id=f"testmqtt-probe-{uuid.uuid4().hex[:6]}")
c.username_pw_set(os.getenv("MQTT_USER"), os.getenv("MQTT_PASS"))
c.tls_set(cert_reqs=ssl.CERT_REQUIRED)
c.on_connect = lambda cl, ud, fl, rc, pr: connected.set() if not rc.is_failure else None
c.on_subscribe = lambda cl, ud, mid, rcs, pr: cl.publish(topic, "ping", qos=1)
c.on_message = lambda cl, ud, msg: received.set()
t0 = time.perf_counter()
try:
c.connect(HOST, PORT, keepalive=10)
c.loop_start()
if not connected.wait(5):
return False
connect_s.set(time.perf_counter() - t0)
t1 = time.perf_counter()
c.subscribe(topic, qos=1)
if not received.wait(5):
return False
roundtrip.observe(time.perf_counter() - t1)
return True
except OSError:
return False
finally:
c.disconnect()
c.loop_stop()
if __name__ == "__main__":
start_http_server(9105) # métricas en http://localhost:9105/metrics
while True:
success.set(1 if probe() else 0)
time.sleep(INTERVAL)Instálalo con pip install paho-mqtt prometheus-client. Abrir una conexión nueva en cada ciclo es intencionado: así se ejercitan DNS, TLS y autenticación cada vez, igual que un dispositivo que se reinicia. Usa un client ID único en cada ejecución. El generador de client IDs explica por qué los duplicados se expulsan entre sí.
Configuración de scrape
global:
scrape_interval: 15s
rule_files: ["/etc/prometheus/alerts.yml"]
scrape_configs:
- job_name: mosquitto
static_configs:
- targets: ["mosquitto-exporter:9234"]
- job_name: mqtt-probe
static_configs:
- targets: ["host.docker.internal:9105"] # donde se ejecuta la sonda
- job_name: emqx
metrics_path: /api/v5/prometheus/stats
static_configs:
- targets: ["emqx:18083"]Elimina los jobs que no uses. En las versiones recientes de EMQX se puede configurar el endpoint de estadísticas para que exija autenticación. En ese caso, añade basic_auth con una API key y su secreto. Para probar EMQX en local, consulta EMQX en Docker. host.docker.internal funciona en Docker Desktop. En Linux, ejecuta la sonda como otro servicio de Compose.
Dashboards que responden preguntas reales
Un dashboard MQTT útil tiene pocos paneles, y cada uno responde una pregunta que alguien hará durante un incidente:
| Panel | PromQL (ejemplo) |
|---|---|
| ¿Hay clientes conectados? | broker_clients_connected o sum(emqx_connections_count) |
| Mensajes de entrada / salida por segundo | rate(broker_messages_received[5m]), rate(emqx_messages_sent[5m]) |
| ¿Estamos descartando mensajes? | rate(broker_publish_messages_dropped[5m]) o rate(emqx_messages_dropped[5m]) |
| ¿Puede conectarse un cliente ahora mismo? | mqtt_probe_success |
| ¿Cómo de lenta es la entrega? | histogram_quantile(0.99, sum by (le) (rate(mqtt_probe_roundtrip_seconds_bucket[10m]))) |
| Tiempo de conexión | mqtt_probe_connect_seconds |
Usa rate() sobre los contadores y nunca grafiques el valor bruto del contador. Pon los paneles de la sonda arriba, porque muestran lo que ven los usuarios. En grafana.com hay dashboards de la comunidad para el exporter de Mosquitto y para EMQX. Son un buen punto de partida, pero comprueba que sus nombres de métricas coinciden con tu versión del exporter.
Reglas de alerta
groups:
- name: mqtt
rules:
- alert: MqttProbeFailing
expr: max_over_time(mqtt_probe_success[3m]) == 0
labels: { severity: page }
annotations:
summary: "No successful MQTT round trip for 3 minutes"
- alert: MqttProbeSlow
expr: histogram_quantile(0.99, sum by (le) (rate(mqtt_probe_roundtrip_seconds_bucket[10m]))) > 0.5
for: 10m
labels: { severity: ticket }
- alert: MqttClientsDropped
expr: broker_clients_connected < 0.7 * (broker_clients_connected offset 10m)
for: 2m
labels: { severity: page }
- alert: MqttExporterDown
expr: up{job="mosquitto"} == 0
for: 2m
labels: { severity: ticket }MqttClientsDropped detecta el patrón clásico de incidente: una gran parte de los dispositivos se desconecta a la vez, tras la caducidad de un certificado, un cambio de red o un reinicio del broker. Enruta las alertas page mediante Alertmanager a tu canal de guardia y las ticket a un sitio más tranquilo. Para comprobaciones externas y los cálculos de SLO, consulta monitorización de disponibilidad MQTT.
Prueba la propia monitorización
Antes de fiarte de las alertas, haz que cada una salte a propósito:
- Fallo de la sonda: cambia
MQTT_PASSpor un valor incorrecto y reinicia la sonda.mqtt_probe_successdebería bajar a 0 yMqttProbeFailingdebería saltar en unos tres minutos. - Caída de clientes: conecta un grupo de clientes de prueba (un simulador de dispositivos o varias pestañas del cliente online), espera diez minutos y ciérralos. El panel de clientes conectados debería bajar en consecuencia.
- Exporter caído:
docker compose stop mosquitto-exporterdebería dispararMqttExporterDown. - Latencia: añade retardo con un network shaper, o compara los números de la sonda con un test de latencia dedicado.
Una alerta que nunca ha saltado en una prueba puede que tampoco salte durante una caída real.
Prometheus y Grafana te dan histórico, dashboards y alertas que $SYS por sí solo no puede ofrecer. Combina las métricas del broker con una sonda del lado del cliente, mantén el dashboard pequeño y prueba cada alerta una vez antes de depender de ella.
Preguntas frecuentes
¿Mosquitto tiene un endpoint de Prometheus integrado?
No. Mosquitto publica sus estadísticas en topics $SYS. Un exporter como mosquitto-exporter se suscribe a $SYS/# y expone los valores en un endpoint HTTP /metrics que Prometheus puede recoger.
¿Puedo monitorizar un broker MQTT gestionado con Prometheus?
Sí, desde el lado del cliente. Aunque no puedas instalar un exporter junto al broker, una pequeña sonda que se conecta, publica y recibe un mensaje puede exportar métricas de éxito y latencia. Suele ser la señal más útil de todas formas.
¿Sobre qué debería alertar primero?
Empieza con tres alertas: la sonda fallando durante unos minutos, una caída brusca de clientes conectados y mensajes descartados por encima de cero. Añade alertas de latencia cuando conozcas tu p99 normal.


