TL;DR:

  • The Grafana + InfluxDB combination handles IoT time-series data more efficiently than general-purpose databases — purpose-built for high write throughput, time-range queries, and automatic data retention policies that keep storage costs manageable
  • Telegraf collects metrics from virtually any sensor protocol (MQTT, Modbus, OPC-UA, HTTP, SNMP) and writes to InfluxDB without custom code — it’s the missing piece between your edge devices and the database
  • InfluxDB 3.0 (2025) introduced Apache Arrow flight protocol and SQL support, making it easier to query and integrate, but InfluxDB 2.x with Flux remains the more documented choice for established edge deployments

Why Time-Series Databases for IoT

Relational databases aren’t designed for the write patterns IoT generates. A sensor reporting every second produces 86,400 data points per day, per sensor. At 100 sensors, that’s 8.64 million rows per day. A PostgreSQL instance handling this without specialist partitioning becomes slow on time-range queries and expensive to maintain as data accumulates.

Time-series databases are optimised for exactly this pattern. They assume most queries ask “give me readings between time A and time B” rather than joining arbitrary tables. InfluxDB organises data by measurement name, tag (indexed metadata), field (the numeric value), and timestamp. It compresses sequential numeric data efficiently, enforces data retention automatically, and handles concurrent writes from many sources without the locking contention general-purpose databases experience.

The TICK Stack Architecture

The original TICK stack (Telegraf, InfluxDB, Chronograf, Kapacitor) has evolved: Grafana has largely replaced Chronograf for dashboards, and modern InfluxDB 2.x handles alerting internally. The practical 2026 stack for edge IoT is:

Sensors → MQTT broker / Protocol adapters

Telegraf (collection agent)

InfluxDB (time-series database)

Grafana (dashboards and alerting)

Each component can run on the same edge gateway or be distributed — Telegraf on the gateway close to sensors, InfluxDB and Grafana on an on-premises server or cloud instance.

Telegraf: Sensor Data Collection Without Custom Code

Telegraf is an agent that runs hundreds of input plugins for different protocols and writes to output plugins (including InfluxDB). For IoT, the most relevant input plugins are:

MQTT: Subscribes to a broker and parses incoming messages as time-series data. Supports JSON, CSV, and custom format parsing.

Modbus: Reads registers from industrial devices over Modbus TCP or Modbus RTU (serial) — the dominant protocol for PLCs, power meters, and HVAC equipment.

OPC-UA: Connects to OPC-UA servers exposed by industrial control systems, historians, and modern PLCs.

HTTP/REST: Polls REST APIs at configurable intervals — useful for cloud-connected sensors or devices that expose HTTP endpoints.

# telegraf.conf — IoT edge configuration

[agent]
  interval = "10s"
  flush_interval = "10s"

# MQTT input: read temperature sensors publishing to broker
[[inputs.mqtt_consumer]]
  servers = ["tcp://localhost:1883"]
  topics = ["sensors/+/temperature", "sensors/+/humidity"]
  qos = 1
  data_format = "json"
  json_time_key = "ts"
  json_time_format = "unix_ms"
  tag_keys = ["device_id", "location"]

# Modbus input: read power meter registers
[[inputs.modbus]]
  name = "power_meter_01"
  controller = "192.168.1.100"
  port = 502
  slave_id = 1
  timeout = "1s"
  configuration_type = "register"
  holding_registers = [
    { name = "active_power_kw", byte_order = "ABCD", data_type = "FLOAT32", address = [0x0001] },
    { name = "voltage_l1",      byte_order = "ABCD", data_type = "FLOAT32", address = [0x0005] },
  ]

# Output: write to InfluxDB 2.x
[[outputs.influxdb_v2]]
  urls = ["http://localhost:8086"]
  token = "$INFLUXDB_TOKEN"
  organization = "edge-monitoring"
  bucket = "sensor_data"

Telegraf handles connection retries, buffering during connectivity outages, and format parsing without custom code. On a typical edge gateway (Raspberry Pi 4 or similar), Telegraf consumes under 50MB of memory collecting data from 50–100 sensors.

InfluxDB Setup and Data Modelling

InfluxDB 2.x uses buckets (equivalent to databases), measurements (equivalent to tables), tags (indexed string metadata), and fields (numeric or string values with timestamps).

# Install InfluxDB 2.x on a Linux edge server
wget -q https://repos.influxdata.com/influxdata-archive_compat.key
cat influxdata-archive_compat.key | gpg --dearmor | tee /etc/apt/trusted.gpg.d/influxdata-archive_compat.gpg
echo 'deb [signed-by=/etc/apt/trusted.gpg.d/influxdata-archive_compat.gpg] https://repos.influxdata.com/debian stable main' | tee /etc/apt/sources.list.d/influxdata.list
apt-get update && apt-get install -y influxdb2

# Start and configure
systemctl start influxdb
influx setup \
  --org edge-monitoring \
  --bucket sensor_data \
  --username admin \
  --password your_secure_password \
  --retention 30d \
  --force

Data retention policies prevent unbounded disk growth. Set them at bucket creation:

# 30-day retention on raw sensor data
influx bucket create --name sensor_data --retention 30d --org edge-monitoring

# 1-year retention on aggregated data (hourly averages)
influx bucket create --name sensor_data_hourly --retention 365d --org edge-monitoring

Use InfluxDB’s built-in task scheduler to downsample: continuously aggregate raw 10-second readings into 1-minute and 1-hour averages and write to the lower-granularity bucket. This keeps storage manageable while preserving long-term trends:

# InfluxDB Flux task: aggregate to hourly means (runs every hour)
option task = {name: "downsample_to_hourly", every: 1h}

from(bucket: "sensor_data")
  |> range(start: -1h)
  |> filter(fn: (r) => r._measurement == "temperature")
  |> aggregateWindow(every: 1h, fn: mean, createEmpty: false)
  |> to(bucket: "sensor_data_hourly", org: "edge-monitoring")

Grafana Dashboard Setup

Grafana connects to InfluxDB as a data source and renders time-series data as graphs, gauges, status maps, and tables.

# Install Grafana on the same server or a separate monitoring host
apt-get install -y grafana
systemctl start grafana-server
# Access at http://localhost:3000, default admin/admin

Adding the InfluxDB data source:

  1. Grafana UI → Connections → Data Sources → Add data source → InfluxDB
  2. Set query language: Flux (for InfluxDB 2.x)
  3. URL: http://localhost:8086
  4. Auth: Token authentication, paste your InfluxDB API token
  5. Organisation and default bucket
  6. Save & Test

Example Grafana panel query (Flux):

from(bucket: "sensor_data")
  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)
  |> filter(fn: (r) => r._measurement == "temperature")
  |> filter(fn: (r) => r.location == "warehouse_floor_1")
  |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)
  |> yield(name: "mean_temperature")

Use template variables in Grafana to make dashboards interactive — let operators select which location, device, or measurement to view without editing the panel query. A variable $location populated from import "influxdata/influxdb/schema" schema.tagValues(bucket: "sensor_data", tag: "location") creates a dropdown of all location tags automatically.

Alerting

Grafana’s alerting system (Grafana Alerting, formerly Grafana 9+ unified alerting) evaluates queries on a schedule and fires alerts through configured notification channels:

# Alert rule example: high temperature warning
# Configured in Grafana UI under Alerting → Alert Rules

Rule name: "Warehouse Temperature Exceeds 35°C"
Query:
  from(bucket: "sensor_data")
    |> range(start: -5m)
    |> filter(fn: (r) => r._measurement == "temperature")
    |> mean()
Condition: WHEN last() OF query IS ABOVE 35
For: 5m  # must be above for 5 minutes before alerting

# Notification channels: PagerDuty, Slack, email, webhook, OpsGenie

For IoT environments, set up separate alert rules for:

  • Sensor data staleness (no data received in X minutes — device may be offline)
  • Threshold breaches (temperature, pressure, vibration above safe ranges)
  • Anomaly detection using InfluxDB’s movingAverage and standard deviation functions

InfluxDB 3.0 vs 2.x: When to Use Which

InfluxDB 3.0 (released mid-2025) is a major architecture change: it replaces the custom Flux query engine with SQL and Apache Arrow, uses Apache Parquet for storage, and introduces a new ingestion protocol. For new deployments in 2026, the choice depends on your team:

InfluxDB 2.xInfluxDB 3.0
Query languageFlux (functional pipeline)SQL + InfluxQL
Grafana supportNative Flux pluginInfluxDB v3 datasource (newer)
DocumentationExtensiveGrowing
Migration from 1.xSupportedRequires re-ingestion
Best forEstablished deployments, Flux expertiseNew projects wanting SQL querying

For teams new to InfluxDB in 2026, InfluxDB 3.0’s SQL interface lowers the learning curve. For teams maintaining existing InfluxDB 2.x deployments, migrating to 3.0 has limited immediate benefit given the documentation and tooling are still maturing.

Minimal Deployment on Raspberry Pi 5

For single-site edge deployments, everything runs on one device:

# Raspberry Pi 5 (8GB) — single-node IoT monitoring stack
# Install all components
apt-get install -y influxdb2 telegraf grafana

# Resource usage at steady state (100 sensors, 10s interval):
# InfluxDB:  ~300MB RAM, 2-5% CPU
# Telegraf:  ~50MB RAM,  1-3% CPU
# Grafana:   ~150MB RAM, <1% CPU (when no users active)
# Storage:   ~2GB/month for 100 sensors at 10s (before compression)
# Compressed: ~200-400MB/month actual disk

This fits comfortably on an SD card or, for reliability, a 64GB USB SSD. For critical deployments, add InfluxDB’s built-in backup schedule and remote storage:

# Daily InfluxDB backup to external storage
influx backup /mnt/backup/influx-$(date +%Y%m%d) --token "$INFLUXDB_TOKEN"

References