Background Mobile

Time-Series Storage for Device Telemetry at Volume

backend development/
September 17, 2026
Time-Series Storage for Device Telemetry at Volume

When your device fleet crosses a few thousand endpoints, the way you store telemetry stops being an infrastructure detail and starts being an architectural decision that affects query latency, storage cost, and how useful your data actually is at 3 AM when something is on fire.

This post covers what we have learned building time-series pipelines for IoT workloads at volume: which storage engines hold up, where they break, and how to think about the decision before you have committed to the wrong one.

What "at volume" actually means

Volume in time-series is not just row count. It is the combination of write throughput, cardinality, and retention window.

A fleet of 10,000 sensors writing every 10 seconds produces 1,000 metrics per second. That sounds manageable. Add 50 fields per payload, and you are talking about 50,000 field writes per second. If each sensor has a unique device ID used as a tag, your cardinality is 10,000. InfluxDB OSS starts showing query degradation when tag cardinality exceeds around 100,000 series; TimescaleDB (Postgres extension) is less sensitive to cardinality but more sensitive to chunk size tuning.

Retention compounds the problem. Keeping raw 10-second samples for 90 days across 10,000 devices produces roughly 7.8 billion rows. Even at 8 bytes per value, that is tens of gigabytes before indexes and metadata. Most teams do not model this out before choosing a datastore.

Which storage engine should you actually use?

There is no universal answer, but the decision tree is shorter than most vendors want you to believe.

Workload Good fit Avoid
High-frequency writes, time-range queries, long retention TimescaleDB, InfluxDB 3.0 (IOx) MongoDB, PostgreSQL without extension
Mixed time-series + relational joins TimescaleDB InfluxDB, QuestDB
Sub-millisecond ingest, columnar analytics QuestDB, Apache Parquet on S3 Row-oriented RDBMS
Federated multi-region with strong consistency CockroachDB + time-series extension InfluxDB Clustered (cost)
Event streams you also want to replay Apache Kafka + Flink, Parquet sink Pure TSDB without replay

TimescaleDB

TimescaleDB partitions Postgres tables into chunks by time interval. Queries that filter by a time range only touch relevant chunks, which keeps execution fast even at billions of rows. Continuous aggregates let you pre-compute hourly or daily rollups without a separate cron job. The catch is that Postgres vacuuming and autovacuum tuning become critical at high ingest rates. You need to size max_wal_size and checkpoint_completion_target carefully, or WAL pressure will stall writes.

InfluxDB 3.0 (Apache Arrow-based IOx)

The IOx rewrite is a significant departure from the TSM storage engine in 1.x and 2.x. It stores data in Apache Parquet format, uses Apache Arrow for query execution, and exposes a SQL interface alongside InfluxQL. Write throughput in benchmarks published by InfluxData shows around 1–2 million points per second on modest hardware. The trade-off: the OSS version of 3.0 has no retention policy UI yet; you manage it via the CLI or API. Clustering is only available in the commercial edition.

QuestDB

QuestDB is worth knowing about if your primary need is append-only ingest and fast ASOF joins across time series. Its columnar storage and SIMD-based query engine can outperform TimescaleDB on pure analytical queries. The ecosystem is smaller, and replication is still maturing as of version 7.x.

How should you structure your ingestion pipeline?

Raw writes from devices should never go directly to your TSDB in production. Put a buffer in between.

The standard pattern is: device publishes to an MQTT broker (Mosquitto or EMQX), a consumer (typically a Go or Rust service) reads off the broker, validates and normalises payloads, then batches writes to the TSDB. Batching is non-negotiable. InfluxDB and TimescaleDB both perform significantly better with batch sizes of 1,000 to 5,000 points compared with one-at-a-time inserts.

For higher-throughput workloads, introduce Apache Kafka between the broker and the writer. This gives you replay capability, backpressure handling, and the ability to fan out to multiple consumers (the TSDB writer, a real-time alerting consumer, a data lake sink) without coupling them.

Schema design matters more than engine choice for many teams. Avoid putting high-cardinality values (device firmware version, user ID, raw error message strings) in tag keys in InfluxDB. In TimescaleDB, normalise those into a separate devices dimension table and join at query time. The write path stays clean; the query planner handles the join efficiently.

/// Not sure where to start?

Get the architecture before you commit

Tell us what you're building and we'll map the technical approach, stack, and rough timeline. No cost, no obligation, no sales call required.

What breaks first at scale?

From experience with fleets in the tens of thousands of devices, the failure modes follow a predictable order.

Cardinality explosion hits first. A developer adds a tag for request correlation IDs or session tokens. Each unique value creates a new series. InfluxDB's TSM engine allocates in-memory index structures per series. At millions of series, the index outgrows available RAM, and ingest stalls.

Chunk bloat is the TimescaleDB equivalent. If your chunk interval is set to the default 7 days and your ingest rate is high, each chunk grows large before it is compressed. Drop the chunk interval to 1 day or even 4 hours for high-throughput tables, enable native compression (timescaledb.compress), and set a compression policy on chunks older than 24 hours. Compression ratios of 90–95% are realistic for sensor data.

Retention policy gaps cause disk exhaustion. Define retention policies before you go to production, not after. Both InfluxDB and TimescaleDB have native retention, but they do not enforce it in real time. Budget for a 20–30% headroom above your projected storage ceiling.

Query timeout cascades happen when a dashboard runs an unbounded SELECT * against a large time range. Enforce query timeout limits at the engine level (statement_timeout in Postgres/TimescaleDB; query limits in InfluxDB Cloud). Materialise common aggregation windows as continuous aggregates rather than computing them on every dashboard load.

Do you need a separate data lake layer?

For operational telemetry dashboards covering the last 7–30 days, a well-tuned TSDB is sufficient. If you need to run ML models over months of raw data, or if compliance requires 5-year retention, a separate cold tier makes more sense.

The practical pattern: hot data lives in your TSDB with a 30-day retention window. A nightly job exports compressed Parquet files to S3 or GCS. Athena or BigQuery sits in front of the cold tier for ad-hoc historical queries. Grafana can query both layers using data source mixing, so analysts get a single interface.

This hybrid approach keeps TSDB storage costs manageable. S3 storage at $0.023 per GB-month is an order of magnitude cheaper than managed TSDB storage, and Athena charges $5 per TB scanned, which is acceptable for infrequent historical queries.

Conclusion

Pick your storage engine based on write pattern, cardinality profile, and whether you need relational joins, not based on which benchmark looks best on a vendor's homepage. Get the ingestion pipeline right before you tune the database. Implement continuous aggregates and chunk compression from day one, not after your disk fills up.

If you are at the point of designing this stack for a new IoT product or re-architecting one that is starting to crack, map your cardinality and write throughput first, then match the engine to those numbers. That one step eliminates most of the regrettable architecture decisions we see in this space.

FAQ

Can InfluxDB handle millions of devices? InfluxDB 3.0 (IOx) scales much better than 1.x and 2.x at high cardinality because it stores data in Parquet files rather than an in-memory series index. Millions of devices is achievable with the commercial cluster, but the OSS single-node version still has practical cardinality limits around 10–50 million series depending on available memory.

When is TimescaleDB the wrong choice? When you need sub-millisecond write latency or when your team cannot manage Postgres operational overhead. TimescaleDB is Postgres, which means WAL, vacuuming, and connection pooling all still apply. For pure write-heavy workloads where SQL join capability is not needed, QuestDB or InfluxDB IOx may be simpler to operate.

How do you handle out-of-order data from devices? Most TSDBs accept late-arriving data within a configurable window. InfluxDB allows writes up to the retention policy boundary. TimescaleDB accepts inserts at any timestamp, though out-of-order writes can delay chunk compression. Design your ingestion service to attach a server-side receipt timestamp alongside the device timestamp so you can distinguish measurement time from arrival time in queries.

What compression ratio should I expect for sensor data? Sensor data compresses well because values often change slowly relative to sample frequency. TimescaleDB's native columnar compression typically achieves 90–95% reduction on float columns with delta-of-delta encoding. InfluxDB IOx (Parquet) achieves similar ratios. Raw JSON payloads stored in a general-purpose database compress far less efficiently and should be avoided for high-frequency telemetry.

Is Kafka overkill for a 1,000-device fleet? Probably yes. At that scale, EMQX or Mosquitto writing directly to a batching ingest service is operationally simpler and cheaper. Kafka becomes worth the overhead when you need multi-consumer fan-out, guaranteed replay, or when your ingest rate exceeds what a single writer thread can batch and flush reliably, typically somewhere above 50,000 messages per second.

Have a project in mind? Contact Sodio Technologies to discuss your requirements and explore the right technology solution for your business.

/// Work with us

Talk to the engineers who'd build it

You'll get a technical scope, timeline and cost estimate from the people doing the work, not an account manager. In-house team, no subcontracting, since 2016.

Contact Us