Your IoT sensors deserve proper observability: MQTT to Prometheus with a Helm chart
May 2024 — because your home produces data worth keeping
Smart home devices generate a constant stream of data. Temperature readings, power consumption, motion events, CO₂ levels, humidity. Most of it disappears into a cloud service or gets forgotten after being displayed on a dashboard for a few seconds.
I wanted that data in Prometheus. Not because Prometheus is trendy, but because it means: Grafana dashboards with real history, alerting when something is wrong, the same tooling I use for everything else in the cluster applied to the physical environment. One observability stack to rule them all.
The bridge between “smart home devices” and “Prometheus metrics” is MQTT + mqtt2prometheus.
The architecture
IoT devices / sensors
│
↓ MQTT publish
MQTT broker (Mosquitto, on your network)
│
↓ subscribe
mqtt2prometheus (one per device class / topic pattern)
│
↓ /metrics endpoint
Prometheus scrape
│
↓ query
Grafana dashboard
mqtt2prometheus is a Go binary that subscribes to MQTT topics and exposes the values as Prometheus metrics. It’s simple and solid. Each instance handles one device class — you configure the topic pattern, how to parse the payload, and what to call the metric.
The deployment complexity is: you might have several of these, each with different topic patterns and metric names, all needing a Prometheus ServiceMonitor. That’s where a Helm chart helps.
What the Helm chart does
Rather than deploying and managing multiple mqtt2prometheus instances by hand, the chart takes a values.yaml with your exporter configurations and generates:
- A
Deploymentfor each exporter (one pod per device class) - A
ConfigMapwith the mqtt2prometheus configuration - A
Serviceexposing the/metricsendpoint - A
ServiceMonitortelling Prometheus where to scrape
One helm upgrade --install to bring up all your exporters at once.
Installing the chart
helm repo add djieno https://helm.djieno.com
helm repo update
helm upgrade --install homekit --values myvalues.yaml djieno/mqtt2prometheus
A values.yaml example
exporters:
- name: homekit-environment
mqttBroker: "tcp://mosquitto.home.svc.cluster.local:1883"
topicPattern: "homekit/sensors/+/environment"
metrics:
- mqttName: temperature
prometheusName: home_temperature_celsius
type: gauge
help: "Room temperature in Celsius"
- mqttName: humidity
prometheusName: home_humidity_percent
type: gauge
help: "Relative humidity percentage"
- name: homekit-power
mqttBroker: "tcp://mosquitto.home.svc.cluster.local:1883"
topicPattern: "homekit/devices/+/power"
metrics:
- mqttName: watts
prometheusName: home_device_power_watts
type: gauge
help: "Device power consumption in watts"
service:
port: 9641
type: ClusterIP
serviceMonitor:
enabled: true
namespace: monitoring
interval: 30s
This creates two mqtt2prometheus deployments — one for environment sensors, one for power monitoring — each with its own Service and ServiceMonitor.
The ServiceMonitor: why it matters
Without a ServiceMonitor, you have to manually add each mqtt2prometheus endpoint to Prometheus’s scrape config. Every time you add an exporter, you edit Prometheus config and reload it.
With a ServiceMonitor (a custom resource from the Prometheus Operator), you declare once: “scrape any Service with these labels every 30 seconds.” The Prometheus Operator picks it up automatically. Adding a new exporter means a helm upgrade with updated values — Prometheus starts scraping it within a minute.
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: homekit-environment
namespace: monitoring
spec:
selector:
matchLabels:
app: mqtt2prometheus
exporter: homekit-environment
endpoints:
- port: metrics
interval: 30s
namespaceSelector:
matchNames:
- home
What you get in Grafana
Once the metrics land in Prometheus, standard Grafana queries work:
# 24-hour temperature history for the living room
home_temperature_celsius{room="living_room"}
# Power consumption over the last week
sum(home_device_power_watts) by (device)
# Alert: CO₂ above 1000ppm for more than 10 minutes
home_co2_ppm > 1000
Grafana’s built-in alerting or Alertmanager can send a notification when the CO₂ climbs (open a window), when a device stops publishing (sensor offline), or when power consumption spikes unexpectedly (someone left the oven on).
The MQTT side
You need something publishing to MQTT. This works with:
- HomeKit accessories via HomeBridge — HomeBridge has MQTT plugins that bridge HomeKit sensor data to MQTT topics
- Zigbee2MQTT — direct bridge between Zigbee sensors and MQTT
- ESPHome / Tasmota — DIY IoT devices that publish to MQTT natively
- Any sensor with MQTT support — the protocol is generic
I run Mosquitto as the broker on the same cluster, deployed as a simple StatefulSet. The IoT devices publish to it; mqtt2prometheus subscribes from it; Prometheus scrapes from mqtt2prometheus. The broker is the only thing that touches the network boundary.
Why not just use a cloud service?
The honest answer is: you can, and for many people it’s fine. Cloud services for smart home data (Apple Home, Google Home, Home Assistant Cloud) work well.
But your home produces data continuously, forever, about where you are and how you live. Having that data in a system you control — where it doesn’t leave your network, where you can query it however you want, where it’s retained for as long as you choose — is worth the setup cost.
The Helm chart makes the setup cost low enough that there’s no good reason not to.


