Telegraf is InfluxData's plugin-based agent for collecting and forwarding metrics, and InfluxDB is a time-series database built for exactly the kind of data sensors produce. Together with an MQTT broker they form a simple IoT pipeline: devices publish JSON readings, Telegraf subscribes and converts them into time-series points, and InfluxDB stores and aggregates them. In this tutorial you will install InfluxDB 2 and Telegraf on Ubuntu 24.04, ingest sensor data from Mosquitto, filter bad readings, and set up retention and hourly downsampling.
Prerequisites
To follow this guide you need:
- A server running Ubuntu 24.04 LTS, for example a CubePath VPS, with a non-root user that has
sudoprivileges. - At least 1 GB of RAM and a few GB of free disk for the database.
- A Mosquitto broker on the same server with password authentication, listening on
127.0.0.1:1883. If your broker uses ACLs, you will give the Telegraf user read access tosensors/#. - Sensors (or a test client) publishing JSON to topics named
sensors/<device>/state, for example:
{"temperature": 21.4, "humidity": 48.2, "battery": 87}
Step 1 - Adding the InfluxData repository
InfluxDB 2 and Telegraf are not in Ubuntu's repositories, so add InfluxData's official APT repository. Download the signing key and look at its fingerprint:
curl -fsSL -o /tmp/influxdata-archive.key https://repos.influxdata.com/influxdata-archive.key
gpg --show-keys --with-fingerprint /tmp/influxdata-archive.key
Compare the fingerprint with the one published in the InfluxData installation documentation. If it matches, convert the key into a keyring and add the repository:
sudo install -m 0755 -d /etc/apt/keyrings
gpg --dearmor < /tmp/influxdata-archive.key | sudo tee /etc/apt/keyrings/influxdata-archive.gpg > /dev/null
echo "deb [signed-by=/etc/apt/keyrings/influxdata-archive.gpg] https://repos.influxdata.com/debian stable main" \
| sudo tee /etc/apt/sources.list.d/influxdata.list
sudo apt update
Step 2 - Installing and initializing InfluxDB 2
Install the InfluxDB 2 server, its command-line client and Telegraf:
sudo apt install influxdb2 influxdb2-cli telegraf
Start InfluxDB and enable it at boot:
sudo systemctl enable --now influxdb
systemctl is-active influxdb
active
InfluxDB listens on port 8086. Keep that port closed in UFW: Telegraf and Grafana on the same server reach it through localhost.
Run the initial setup as your regular user. It creates the admin account, an organization called iot, and a bucket called iot that keeps raw data for 30 days. Replace your_strong_password with a real password:
influx setup \
--username admin \
--password 'your_strong_password' \
--org iot \
--bucket iot \
--retention 30d \
--force
User Organization Bucket
admin iot iot
The command also saves an operator token in ~/.influxdbv2/configs, so the next influx commands work without extra flags. Confirm the bucket and note its ID:
influx bucket list --name iot
ID Name Retention Shard group duration Organization ID Schema Type
4f2c1a9e8d7b6a50 iot 720h0m0s 24h0m0s a1b2c3d4e5f60718 implicit
Step 3 - Creating credentials for Telegraf
Telegraf should not use the operator token. Create a token that can only write to the iot bucket, replacing your_bucket_id with the ID from the previous step:
influx auth create --org iot --write-bucket your_bucket_id --description "telegraf write"
Copy the value in the Token column. Then create an MQTT user for Telegraf on the broker; the command prompts for a password:
sudo mosquitto_passwd /etc/mosquitto/passwd telegraf
sudo chown mosquitto:mosquitto /etc/mosquitto/passwd
sudo systemctl restart mosquitto
If the broker uses an ACL file, add these lines to it and restart Mosquitto again:
user telegraf
topic read sensors/#
Store both secrets in the environment file that the Telegraf systemd unit loads, so they do not appear in the configuration files:
sudo nano /etc/default/telegraf
INFLUX_TOKEN=your_telegraf_write_token
MQTT_PASSWORD=your_mqtt_password
sudo chmod 0600 /etc/default/telegraf
Step 4 - Configuring Telegraf
The default /etc/telegraf/telegraf.conf enables CPU, disk and memory inputs and an InfluxDB 1.x output that does not work with InfluxDB 2. Keep a copy and replace it with a short agent configuration:
sudo mv /etc/telegraf/telegraf.conf /etc/telegraf/telegraf.conf.orig
sudo nano /etc/telegraf/telegraf.conf
[agent]
interval = "10s"
flush_interval = "10s"
metric_batch_size = 1000
metric_buffer_limit = 10000
omit_hostname = true
[[outputs.influxdb_v2]]
urls = ["http://127.0.0.1:8086"]
token = "${INFLUX_TOKEN}"
organization = "iot"
bucket = "iot"
metric_buffer_limit is how many points Telegraf keeps in memory while InfluxDB is unreachable; 10,000 points covers several minutes for a few hundred sensors.
Now define the MQTT input in its own file:
sudo nano /etc/telegraf/telegraf.d/mqtt-sensors.conf
[[inputs.mqtt_consumer]]
servers = ["tcp://127.0.0.1:1883"]
topics = ["sensors/+/state"]
qos = 1
client_id = "telegraf-iot"
persistent_session = true
username = "telegraf"
password = "${MQTT_PASSWORD}"
# Store every reading in a measurement called "environment"
name_override = "environment"
# Do not add the full topic as a tag
topic_tag = ""
data_format = "json"
# sensors/<device>/state -> tag device=<device>
[[inputs.mqtt_consumer.topic_parsing]]
topic = "sensors/+/state"
tags = "_/device/_"
A few settings deserve an explanation:
qos = 1withpersistent_session = trueand a fixedclient_idmakes the broker queue messages while Telegraf is restarting, so readings are not lost during upgrades.- The JSON parser turns every numeric key into a field, so the example payload produces the fields
temperature,humidityandbattery. String values are ignored unless you list them injson_string_fields. topic_parsingturns the second segment of the topic into adevicetag, which is what you will filter and group by in queries and dashboards.
Step 5 - Filtering invalid readings
Cheap sensors sometimes report nonsense such as -127 °C when a wire is loose. Drop those points before they reach the database with a small Starlark processor:
sudo nano /etc/telegraf/telegraf.d/filter.conf
[[processors.starlark]]
namepass = ["environment"]
source = '''
def apply(metric):
t = metric.fields.get("temperature")
if t != None and (t < -40.0 or t > 85.0):
return None
h = metric.fields.get("humidity")
if h != None and (h < 0.0 or h > 100.0):
return None
return metric
'''
Returning None drops the metric; returning metric passes it through unchanged.
Step 6 - Testing and starting Telegraf
Test the configuration before starting the service. The --test flag prints metrics instead of writing them, and --test-wait keeps service inputs such as MQTT listening for a few seconds. The environment file is loaded first so the variables are set:
sudo bash -c 'set -a && . /etc/default/telegraf && telegraf --config /etc/telegraf/telegraf.conf --config-directory /etc/telegraf/telegraf.d --test --test-wait 20'
While it waits, publish a reading from a second terminal:
mosquitto_pub -h 127.0.0.1 -u telegraf -P 'your_mqtt_password' \
-t 'sensors/livingroom/state' -m '{"temperature": 21.4, "humidity": 48.2, "battery": 87}'
NoteThis test publish works only if the
telegrafuser may write that topic. With the ACL from the Mosquitto guide, publish as the device's own user instead (for examplesensor01tosensors/sensor01/state).
Telegraf prints the parsed point in line protocol:
> environment,device=livingroom battery=87,humidity=48.2,temperature=21.4 1790330042000000000
Publish {"temperature": -127} in the same way and nothing is printed, which shows the filter works. Start the service:
sudo systemctl enable --now telegraf
sudo systemctl restart telegraf
sudo journalctl -u telegraf -n 20 --no-pager
... I! Loaded processors: starlark
... I! Loaded outputs: influxdb_v2
... I! Loaded inputs: mqtt_consumer
... I! [inputs.mqtt_consumer] Connected [tcp://127.0.0.1:1883]
Step 7 - Querying the data
After a few readings arrive, query the last ten minutes of data with Flux:
influx query 'from(bucket: "iot")
|> range(start: -10m)
|> filter(fn: (r) => r._measurement == "environment" and r._field == "temperature")
|> last()'
Result: _result
Table: keys: [_start, _stop, _field, _measurement, device]
... _field _measurement device _value _time
... temperature environment livingroom 21.4 2026-09-25T10:20:42Z
Each device appears as its own table because device is a tag.
Step 8 - Adding long-term storage with downsampling
Raw readings every few seconds are useful for recent graphs but expensive to keep for years. Keep them for 30 days in iot and store hourly averages for 400 days in a second bucket:
influx bucket create --name iot_hourly --org iot --retention 400d
Create an InfluxDB task that runs every hour, averages the previous hour and writes the result to iot_hourly. Save the Flux script to a file:
nano ~/downsample-hourly.flux
option task = {name: "iot-downsample-hourly", every: 1h, offset: 5m}
from(bucket: "iot")
|> range(start: -task.every)
|> filter(fn: (r) => r._measurement == "environment")
|> aggregateWindow(every: 1h, fn: mean, createEmpty: false)
|> to(bucket: "iot_hourly", org: "iot")
The offset: 5m delays each run by five minutes so late messages from the previous hour are included. Register the task:
influx task create --org iot --file ~/downsample-hourly.flux
influx task list
ID Name Organization ID Organization Status Every Cron
0d9e8f7a6b5c4d30 iot-downsample-hourly a1b2c3d4e5f60718 iot active 1h
After the next full hour, check the runs and the new data:
influx task run list --task-id your_task_id
influx query 'from(bucket: "iot_hourly") |> range(start: -1d) |> limit(n: 3)'
A run with status success and rows in iot_hourly mean downsampling works. Point dashboards that show months of data at iot_hourly and recent views at iot.
Troubleshooting
No data in InfluxDB, and the journal shows 401 Unauthorized. The token in /etc/default/telegraf is wrong or lacks write access to the bucket. List tokens with influx auth list and recreate it if needed, then restart Telegraf.
Telegraf connects to MQTT but no metrics arrive. Subscribe as the telegraf user to confirm the broker delivers messages to it:
mosquitto_sub -h 127.0.0.1 -u telegraf -P 'your_mqtt_password' -t 'sensors/#' -v
If nothing shows up, the ACL does not grant read sensors/#, or devices publish to a different topic than sensors/+/state.
Journal shows invalid character or JSON parse errors. A device publishes a payload that is not a JSON object, for example a bare number. Either fix the device or give that topic its own inputs.mqtt_consumer block with data_format = "value" and data_type = "float".
metric buffer overflow warnings. InfluxDB was unreachable long enough to fill the buffer. Check systemctl status influxdb and disk space; raise metric_buffer_limit only if outages are expected.
Conclusion
You now have a working IoT pipeline: Telegraf subscribes to Mosquitto, tags each reading with its device, drops invalid values and writes to InfluxDB, where a task keeps a compact hourly history. Next, connect Grafana to InfluxDB with a read-only token to build dashboards and alerts, add more sensors simply by publishing to sensors/<device>/state, and back up InfluxDB regularly with influx backup.
