An MQTT broker only passes messages along. Once a temperature reading has been delivered, it is gone, apart from the last retained value. If you want to see how the living room temperature changed over the past week, something has to subscribe to the topics, store every reading with a timestamp and draw a chart from it.

A common open source stack for this is Telegraf, InfluxDB and Grafana. Telegraf subscribes to MQTT and converts messages into metrics, InfluxDB stores them as time series, and Grafana draws the dashboards. This guide uses the current generation of each: InfluxDB 3 Core, the Telegraf influxdb_v3 output and the InfluxDB data source that ships with Grafana. It runs everything in Docker Compose next to an existing Mosquitto broker. Every command and setting below comes from the official documentation linked at the end.

What we start from

The example assumes sensors publish one plain number per topic, laid out as home/<room>/<device>/<quantity>. That is what the MicroPython ESP32 sensor from our earlier guide does:

home/livingroom/esp32/temperature  21.4
home/livingroom/esp32/humidity     48.2

The broker runs at 192.168.1.10:1883 with a username and password. Adjust the names to match your own setup.

Step 1: InfluxDB 3 Core

InfluxDB 3 Core is the open source edition of InfluxDB 3. Its official Docker image is influxdb:3-core, available for x86_64 and ARM64, and its HTTP API listens on port 8181. Note that InfluxData has announced that the plain latest tag of the influxdb image will point to InfluxDB 3 Core from 15 September 2026, so if you run an older InfluxDB in Docker, pin a specific tag.

Create a project directory with this compose.yaml (Telegraf and Grafana come later):

services:
  influxdb3-core:
    image: influxdb:3-core
    ports:
      - "127.0.0.1:8181:8181"
    command:
      - influxdb3
      - serve
      - --node-id=node0
      - --object-store=file
      - --data-dir=/var/lib/influxdb3/data
    volumes:
      - influxdb3-data:/var/lib/influxdb3/data
    restart: unless-stopped

volumes:
  influxdb3-data:

The serve options match the Docker Compose example in the InfluxDB 3 Core setup guide. The port mapping to 127.0.0.1 is deliberate. The documentation warns that until the first admin token exists, the token endpoint accepts unauthenticated requests, and anyone who can reach the port in that window can claim the operator token. Binding to localhost first is one of the fixes the guide recommends. The other containers will reach InfluxDB over the internal Compose network anyway.

Start it and create the admin token:

docker compose up -d influxdb3-core
docker compose exec influxdb3-core influxdb3 create token --admin

The token is shown only once and cannot be retrieved later, so store it in a password manager. Then create a database. Retention is optional, but InfluxDB 3 Core only lets you set it when the database is created, so decide now. This keeps one year of data:

docker compose exec influxdb3-core influxdb3 create database \
  --retention-period 1y --token YOUR_TOKEN home

By default data never expires. Also note that Core allows at most five databases.

Step 2: Telegraf

Create telegraf.conf next to compose.yaml:

[[inputs.mqtt_consumer]]
  servers = ["tcp://192.168.1.10:1883"]
  topics = ["home/+/+/temperature", "home/+/+/humidity"]
  username = "telegraf"
  password = "${MQTT_PASSWORD}"
  qos = 0
  topic_tag = ""
  data_format = "value"
  data_type = "float"

  [[inputs.mqtt_consumer.topic_parsing]]
    measurement = "_/_/_/measurement"
    tags = "_/room/device/_"

[[outputs.influxdb_v3]]
  urls = ["http://influxdb3-core:8181"]
  token = "${INFLUX_TOKEN}"
  database = "home"

What each part does:

  • mqtt_consumer subscribes to the listed topics. + matches exactly one topic level, so these two filters pick up temperature and humidity from any room and device, but not, for example, a status topic with the text online.
  • data_format = "value" reads a payload consisting of a single value. The data_type option is required; with float, a payload that cannot be converted to a number causes an error instead of silently producing a text field.
  • topic_parsing turns parts of the topic into a measurement name and tags. In the pattern, _ means "ignore this level". home/livingroom/esp32/temperature becomes the measurement temperature with tags room=livingroom and device=esp32, and the reading lands in a field called value.
  • topic_tag = "" stops Telegraf from adding the whole topic as an extra tag, which the parsed tags make redundant.
  • outputs.influxdb_v3 writes to InfluxDB 3 Core through its native API. This output needs Telegraf 1.38 or later. With older versions, the InfluxDB documentation shows how to use the influxdb_v2 output against the v2 compatibility API instead.

Telegraf replaces ${...} with environment variables, which keeps the passwords out of the file. Put them in a .env file next to compose.yaml (and keep it out of version control):

INFLUX_TOKEN=YOUR_TOKEN
MQTT_PASSWORD=your-mqtt-password

Then add Telegraf to compose.yaml under services. The official image expects the configuration at /etc/telegraf/telegraf.conf:

  telegraf:
    image: telegraf
    volumes:
      - ./telegraf.conf:/etc/telegraf/telegraf.conf:ro
    environment:
      - INFLUX_TOKEN=${INFLUX_TOKEN}
      - MQTT_PASSWORD=${MQTT_PASSWORD}
    depends_on:
      - influxdb3-core
    restart: unless-stopped

Start it, watch the log for errors and check that rows are arriving:

docker compose up -d telegraf
docker compose logs -f telegraf
docker compose exec influxdb3-core influxdb3 query --database home \
  --token YOUR_TOKEN 'SELECT * FROM temperature ORDER BY time DESC LIMIT 5'

One side effect is worth knowing about. If your sensors publish retained messages, the broker sends the last retained value of each topic every time Telegraf connects. After every Telegraf restart you will therefore get one extra point per topic, timestamped at the moment of the reconnect. For room temperatures this hardly matters; for counters it might.

Step 3: Grafana

Grafana now publishes its open source image as grafana/grafana (the documentation notes that grafana/grafana-oss is no longer updated from release 12.4.0). Add it to compose.yaml:

  grafana:
    image: grafana/grafana
    ports:
      - "3000:3000"
    volumes:
      - grafana-data:/var/lib/grafana
    restart: unless-stopped

and add grafana-data: under volumes: at the bottom. Start it with docker compose up -d grafana, open http://SERVER_ADDRESS:3000 and sign in as admin with the password admin. Grafana asks you to change it straight away.

The InfluxDB data source is built into Grafana. You can query InfluxDB 3 Core with SQL or InfluxQL. SQL goes over Flight SQL (gRPC) and, without TLS, requires the Insecure Connection option. InfluxQL uses plain HTTP, which is the simpler choice for a home network:

  1. Click Connections in the left menu, then Data sources and Add new data source, and select InfluxDB.
  2. In URL, enter http://influxdb3-core:8181. Grafana runs on the same Compose network, so the service name works as a hostname.
  3. In Product, select the InfluxDB 3 product. Grafana's documentation lists InfluxDB OSS 3.x; InfluxData's guide, written for a version of the plugin without a Core entry, says to choose InfluxDB Enterprise 3.x. Either way, the settings that follow are the same.
  4. In Query language, select InfluxQL.
  5. In the database settings, set Database to home, User to any non-empty value, Password to your token and HTTP Method to POST.
  6. Click Save & test.

InfluxDB 3 Core has only admin tokens, so Grafana is using a token with full rights. That is another reason not to expose Grafana or InfluxDB to the internet.

To build a first chart, create a dashboard and add a visualization with the new data source. In the InfluxQL query builder, set FROM to temperature, select the value field with the mean function, and add room to GROUP BY next to the default time grouping. You get one line per room. Repeat with humidity in a second panel.

Maintenance tips

  • Back up the influxdb3-data and grafana-data volumes along with telegraf.conf, compose.yaml and the token.
  • Pin versions once everything works. influxdb:3-core always points to the latest 3.x release; the documentation also shows tags such as influxdb:3.12.0-core.
  • Add sensors without touching Telegraf by keeping to the same topic layout. A new room or device simply appears as a new tag value.
  • Check Telegraf's log after changing the payload format of any device: a text value on a subscribed topic shows up as a parse error.