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 sudo privileges.
  • 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 to sensors/#.
  • 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 = 1 with persistent_session = true and a fixed client_id makes 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, humidity and battery. String values are ignored unless you list them in json_string_fields.
  • topic_parsing turns the second segment of the topic into a device tag, 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}'

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.