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_consumersubscribes 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, astatustopic with the textonline.data_format = "value"reads a payload consisting of a single value. Thedata_typeoption is required; withfloat, a payload that cannot be converted to a number causes an error instead of silently producing a text field.topic_parsingturns parts of the topic into a measurement name and tags. In the pattern,_means "ignore this level".home/livingroom/esp32/temperaturebecomes the measurementtemperaturewith tagsroom=livingroomanddevice=esp32, and the reading lands in a field calledvalue.topic_tag = ""stops Telegraf from adding the whole topic as an extra tag, which the parsed tags make redundant.outputs.influxdb_v3writes 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 theinfluxdb_v2output 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:
- Click Connections in the left menu, then Data sources and Add new data source, and select InfluxDB.
- In URL, enter
http://influxdb3-core:8181. Grafana runs on the same Compose network, so the service name works as a hostname. - 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.
- In Query language, select InfluxQL.
- In the database settings, set Database to
home, User to any non-empty value, Password to your token and HTTP Method to POST. - 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-dataandgrafana-datavolumes along withtelegraf.conf,compose.yamland the token. - Pin versions once everything works.
influxdb:3-corealways points to the latest 3.x release; the documentation also shows tags such asinfluxdb: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.