Skip to content
Learni
View all tutorials
DevOps IoT

How to Implement Grafana for IoT in 2026

Lire en français

Introduction

In 2026, Grafana remains the go-to tool for visualizing real-time IoT data, natively integrating MQTT, InfluxDB, and Prometheus for interactive dashboards. This expert tutorial guides you through implementing a production-ready stack: Dockerized deployment, automated provisioning of datasources and dashboards, alerting on sensor anomalies (temperature, humidity, vibrations), and horizontal scaling. Why it matters: IoT fleets generate terabytes of time series data; Grafana excels at aggregating them with Loki queries for logs and geospatial panels for tracking. You'll learn to avoid latency traps in high-frequency data, secure with OAuth, and provision via GitOps. Result: pro monitoring that alerts in <5s on critical thresholds, bookmark-worthy for any senior IoT DevOps engineer. (128 words)

Prerequisites

  • Docker 27+ and Docker Compose 2.29+
  • Advanced knowledge of YAML provisioning and InfluxDB 3.x
  • Access to an MQTT broker (Mosquitto 2.1+) and simulated sensors
  • Node.js 22+ for Grafana API tests
  • Production Linux system (Ubuntu 24.04 recommended)

Docker Compose Stack Deployment

docker-compose.yml
version: '3.8'
services:
  grafana:
    image: grafana/grafana-enterprise:11.1.0
    container_name: grafana-iot
    ports:
      - '3000:3000'
    volumes:
      - grafana-data:/var/lib/grafana
      - ./provisioning:/etc/grafana/provisioning
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin123!
      - GF_AUTH_ANONYMOUS_ENABLED=false
      - GF_SERVER_ROOT_URL=http://localhost:3000
    restart: unless-stopped
    depends_on:
      - influxdb
      - mosquitto
  influxdb:
    image: influxdb:3.0
    container_name: influxdb-iot
    ports:
      - '8086:8086'
    volumes:
      - influxdb-data:/var/lib/influxdb3
    environment:
      - DOCKER_INFLUXDB_INIT_MODE=setup
      - DOCKER_INFLUXDB_INIT_USERNAME=iot-admin
      - DOCKER_INFLUXDB_INIT_PASSWORD=securepass123
      - DOCKER_INFLUXDB_INIT_ORG=iot-org
      - DOCKER_INFLUXDB_INIT_BUCKET=iot-sensors
      - DOCKER_INFLUXDB_INIT_TOKEN=iot-token-abc123
    restart: unless-stopped
  mosquitto:
    image: eclipse-mosquitto:2.0
    container_name: mosquitto-iot
    ports:
      - '1883:1883'
      - '9001:9001'
    volumes:
      - ./mosquitto.conf:/mosquitto/config/mosquitto.conf
    restart: unless-stopped
volumes:
  grafana-data:
  influxdb-data:

This docker-compose.yml deploys Grafana Enterprise 11.1, InfluxDB 3 for IoT time series, and Mosquitto MQTT. Volumes persist data; provisioning mounts a folder for automated configs. Run with docker compose up -d. Pitfall: forget depends_on, and InfluxDB won't be ready for the datasource.

Mosquitto Configuration for MQTT

Create mosquitto.conf to enable WebSocket (port 9001) and basic auth, essential for the Grafana MQTT plugin. This enables real-time subscriptions without polling, reducing latency to <100ms for high-frequency sensor data.

Secure Mosquitto MQTT Config

mosquitto.conf
persistence true
persistence_location /mosquitto/data/
log_dest file /mosquitto/log/mosquitto.log
listener 1883
listener 9001
protocol websockets
allow_anonymous true
max_queued_messages 10000
message_size_limit 0
max_inflight_messages 1000

Enables persistence and WebSockets for Grafana. max_queued_messages handles IoT bursts (e.g., 10k events/s from a fleet). In production, set allow_anonymous false with users/password. Test with mosquitto_sub -h localhost -t iot/sensor/#.

Provisioning Grafana Datasources

Use YAML provisioning for GitOps: datasources auto-created on boot. InfluxDB for historical metrics, MQTT for live streams. Analogy: like Terraform for infra, but for Grafana UI.

InfluxDB Datasource Provisioning

provisioning/datasources/influxdb.yml
apiVersion: 1
providers:
  - name: 'iot-influxdb'
    orgId: 1
    folder: ''
    type: influxdb
    uid: influxdb-iot
    url: http://influxdb:8086
    access: proxy
    database: iot-sensors
    user: iot-admin
    secureJsonData:
      password: 'securepass123'
    jsonData:
      version: Flux
      organization: iot-org
      defaultBucket: iot-sensors
      tlsSkipVerify: true
    editable: false

Configures InfluxDB 3.x with Flux queries for IoT time series. proxy mode caches queries; editable: false prevents UI changes. Set editable: true in dev. Check Grafana logs for 'datasource added'.

MQTT Datasource Provisioning

provisioning/datasources/mqtt.yml
apiVersion: 1
providers:
  - name: 'iot-mqtt'
    orgId: 1
    folder: ''
    type: mqtt
    uid: mqtt-iot
    url: ws://mosquitto:9001
    access: proxy
    jsonData:
      sharedTopic: false
      keepAlive: 60
    secureJsonData:
      user: iot-user
      password: mqttpass
    editable: false

Grafana's native MQTT plugin for live subscriptions (topic iot/sensor/+). WebSocket URL for browser compatibility. keepAlive 60s handles flaky IoT reconnections. Install plugin with grafana-cli plugins install grafana-mqtt-datasource if not Enterprise.

Real-Time IoT Dashboard

Import an expert JSON dashboard with variables (device ID), gauge/stat panels for sensors, heatmap for trends. Flux queries for aggregations (mean temp per hour).

IoT Sensors Dashboard JSON

provisioning/dashboards/iot-sensors.json
{
  "__inputs": [],
  "id": null,
  "title": "IoT Sensors Dashboard",
  "tags": [],
  "style": "dark",
  "timezone": "browser",
  "panels": [
    {
      "id": 1,
      "title": "Température Live",
      "type": "timeseries",
      "targets": [
        {
          "refId": "A",
          "datasource": {
            "type": "mqtt",
            "uid": "mqtt-iot"
          },
          "topic": "iot/sensor/$device/temp",
          "measurements": ["value"]
        }
      ],
      "fieldConfig": {
        "defaults": {
          "unit": "celsius",
          "min": -20,
          "max": 80
        }
      },
      "gridPos": { "h": 8, "w": 12, "x": 0, "y": 0 }
    },
    {
      "id": 2,
      "title": "Historique Temp/Humidité",
      "type": "timeseries",
      "targets": [
        {
          "refId": "A",
          "datasource": {
            "type": "influxdb",
            "uid": "influxdb-iot"
          },
          "query": "from(bucket: "iot-sensors")
  |> range(start: v.timeRangeStart)
  |> filter(fn: (r) => r._measurement == "sensor")
  |> filter(fn: (r) => r._field == "temp" or r._field == "humidity")
  |> aggregateWindow(every: v.windowPeriod, fn: mean)",
          "format": "time_series"
        }
      ],
      "gridPos": { "h": 8, "w": 12, "x": 12, "y": 0 }
    }
  ],
  "time": {
    "from": "now-1h",
    "to": "now"
  },
  "timepicker": {},
  "variables": [
    {
      "name": "device",
      "label": "Device ID",
      "type": "custom",
      "options": [
        { "text": "sensor1", "value": "sensor1" },
        { "text": "sensor2", "value": "sensor2" }
      ],
      "current": { "selected": true, "text": "sensor1", "value": "sensor1" }
    }
  ],
  "refresh": "10s",
  "schemaVersion": 39,
  "version": 1,
  "revision": 1
}

Complete dashboard with MQTT live panel and InfluxDB Flux query for history. Variable $device for multi-sensors. 10s refresh for near-real-time; auto-scaling Celsius units. Provision via dashboards/ folder and restart Grafana.

MQTT Publisher Test Script

Simulate sensor data to validate: Python script publishes random temp/humidity to topics.

Python IoT Data Publisher

iot_publisher.py
import paho.mqtt.client as mqtt
import json
import random
import time

BROKER = "localhost"
PORT = 1883
TOPICS = ["iot/sensor/sensor1/temp", "iot/sensor/sensor1/humidity"]

client = mqtt.Client()
client.connect(BROKER, PORT, 60)

while True:
    for topic in TOPICS:
        field = topic.split('/')[-1]
        if field == 'temp':
            value = round(random.uniform(20, 35), 2)
        else:
            value = round(random.uniform(40, 70), 2)
        payload = json.dumps({"device": "sensor1", "value": value, "ts": time.time()})
        client.publish(topic, payload)
        print(f"Published {topic}: {payload}")
    time.sleep(5)

client.loop_forever()

Uses Paho MQTT for IoT-like data bursts. InfluxDB can sink via Telegraf (add it). json.dumps for structured payloads; 5s loop simulates 12 samples/min. Run pip install paho-mqtt then python iot_publisher.py.

Alerting Rules Provisioning

provisioning/alerting/alerting.yml
apiVersion: 1
groups:
  - orgId: 1
    name: IoT Alerts
    folder: IoT
    interval: 30s
    rules:
      - uid: temp-high
        title: "Température capteur élevée"
        condition: "B"
        data:
          - refId: A
            datasourceUid: influxdb-iot
            model: flux
            relativeTimeRange:
              from: 600
              to: 0
            queryText: |
              from(bucket: "iot-sensors")
                |> range(start: -10m)
                |> filter(fn: (r) => r._measurement == "sensor")
                |> filter(fn: (r) => r._field == "temp")
                |> last()
          - refId: B
            conditions:
              - evaluator:
                  params: [35]
                operator:
                  type: gt
                reducer:
                  type: last
                type: query
        noDataState: NoData
        execErrState: Error
        for: 1m
        annotations:
          summary: "Temp > 35°C sur {{ $labels.device }}"
        labels:
          severity: critical

Rule alerts if last temp >35°C for 1m. Flux last() for current value; Jinja annotations for Slack/Teams notifications. for: 1m prevents flapping. Link to contact points via UI after provisioning.

Best Practices

  • GitOps Provisioning: Store YAML/JSON in Git, sync with grafana-toolbox for CI/CD.
  • Horizontal Scaling: Deploy Grafana in HA cluster (multi-replicas + shared PostgreSQL DB).
  • IoT Security: Enable mTLS on MQTT, OAuth2 for datasources, and rate-limit Grafana API.
  • Query Optimization: Use InfluxDB downsampling (e.g., 1s→1m) for legacy dashboards.
  • Backups: Cron grafana-cli admin reset-admin-password + S3 for dashboard snapshots.

Common Errors to Avoid

  • Datasource Timing: InfluxDB not ready → wait 30s post-boot or add Docker healthchecks.
  • MQTT Reconnects: Without keepAlive, reconnections fail on flaky IoT networks; set 60s+.
  • Dashboard Refresh: <5s kills perf (100% CPU); cap at 10s + MQTT WebSockets.
  • Alert Flapping: Forget for: 0 → notification spam; always set min duration.

Next Steps

Master Grafana Loki for IoT logs or Prometheus + Node Exporter for edge metrics. Check out Learni DevOps & IoT training for advanced certifications. Official docs: Grafana IoT docs. GitHub repo example: clone this tutorial to fork.