---
title: " Fleet Telemetry Database: Store & Query at Scale"
description: "GPS, OBD-II, and CAN bus data is a time-series workload. How to model, store, and query fleet telemetry at scale with PostgreSQL."
section: "Postgres for IoT"
---

> **TimescaleDB is now Tiger Data.**

A 100-vehicle fleet reporting GPS position and 10 OBD-II signals at 1 Hz generates roughly 4 million data points per hour. Scale that to 1,000 vehicles and you're at 40 million per hour - roughly 950 million rows per day. The database architecture you choose for your fleet platform determines whether dashboard queries complete in milliseconds or minutes at that scale, and whether you can run driver behavior analytics at all.

This page covers what fleet telemetry data actually looks like, why it's a time-series workload, why general-purpose databases struggle at fleet scale, and how to choose and design a database architecture that handles it. Tiger Data builds [<u>Tiger Cloud</u>](https://www.tigerdata.com/cloud), one of the databases compared below. The goal is to give you enough context to evaluate the options - including cases where Tiger Cloud is not the right fit.

## What is fleet telemetry data?

Fleet telemetry is the continuous collection and transmission of sensor and event data from vehicles to a central system. Each vehicle reports its position, speed, engine state, and fault codes in real time, producing a timestamped, append-only stream of readings that accumulates at millions of rows per day across a modern fleet.

This is distinct from "fleet management software" - the application layer built on top. This page is about the database behind it.

A modern connected vehicle fleet produces four primary data types:

**Location data.** GPS pings (latitude, longitude, altitude, heading) at 1- to 30-second intervals per vehicle. High frequency, moderate size per record.

**OBD-II diagnostics.** Vehicle speed, engine RPM, throttle position, fuel level, engine coolant temperature, fault codes. Available on every vehicle built after 1996. Typically sampled at 1 Hz during operation.

**CAN bus messages.** Raw ECU signals: brake pressure, steering angle, transmission state, airbag sensors, wheel speed per axle. Available on vehicles with CAN logger hardware (a telematics gateway or dedicated data logger). Message rates vary by bus - from a few hundred frames per second on low-speed CAN to several thousand on high-speed CAN.

**Driver behavior events.** Hard braking, rapid acceleration, sharp cornering, idle time. Either derived on-device from accelerometer data or computed server-side from raw OBD-II streams.

EV fleets add a second layer on top of these: battery management system (BMS) signals. These include state of charge (SoC), state of health (SoH), cell temperature by module, and charge session events. SoH changes slowly - over weeks and months - but requires high-frequency historical data to compute accurately. SoC changes continuously during driving and charging.

All of this data shares two structural characteristics that define how it should be stored:

Every record is timestamped at collection. Data is append-only - historical readings don't change. Analytical queries are almost always time-range bounded ("show me this vehicle's speed for the last 4 hours"). That's the time-series profile.

The second characteristic is cardinality. `vehicle_id` is a high-cardinality dimension. A fleet of 10,000 vehicles means 10,000 distinct time series flowing into the same table simultaneously. How a database handles that cardinality determines whether it scales or falls over.

## Why standard databases struggle at fleet scale

This isn't an argument that general-purpose databases are bad - they're not. The architecture question matters at hundreds of vehicles and above. A small fleet under 50 vehicles at low sample frequency may run fine on standard PostgreSQL without any extensions.

At scale, three problems compound:

**Sequential scan growth.** A 100-vehicle fleet at 1 Hz generates ~95M rows per day. Without time-based partitioning, a 30-day query must touch all 30 days of data - even if you only need one. Query cost grows linearly with history. There's no shortcut without explicit partitioning.

**Index bloat on high-cardinality dimensions.** Indexing (`vehicle_id`, `time`) in a general-purpose RDBMS creates write amplification at high vehicle counts. Each vehicle writes to the same shared index structure. As cardinality grows, so does the overhead of maintaining that index.

**Storage inefficiency on redundant readings.** A vehicle parked for eight hours sends many near-identical readings. Standard row storage retains every value at full fidelity. Time-series engines use delta-of-delta encoding on sorted time columns - a parked vehicle's readings compress dramatically because the differences between consecutive values are near-zero. Compression ratios of [<u>90-98%</u>](https://www.tigerdata.com/docs/build/how-to/basic-compression) are typical on real fleet data.

**Aggregation cost at dashboard query time.** Fleet dashboards need "average speed per vehicle per hour" or "total idle time per driver per day." Without pre-computed aggregates, every dashboard load rescans the raw table. A fleet operations dashboard with 50 concurrent users rescans it 50 times, simultaneously.

A [<u>time-series database</u>](https://www.tigerdata.com/learn/time-series-database-what-it-is-how-it-works-and-when-you-need-one) addresses all four of these with its core architecture: time-based partitioning, columnar compression, and incremental materialized views.

## Database options for fleet telemetry

Four architectural approaches are worth understanding before looking at specific products:

**Time-series database.** Purpose-built for timestamped, high-cardinality, append-only data. Usually the right fit for fleet telemetry backends. The partitioning, compression, and aggregation primitives are built in.

**General-purpose RDBMS.** Works for small fleets or mixed workloads where fleet telemetry is one of several use cases. Struggles at high vehicle counts without explicit partitioning and extension support.

**Document store.** MongoDB is sometimes chosen for flexible schema. The tradeoff: no SQL join capability for vehicle metadata, and no native time-partitioning or geospatial integration at the level PostGIS provides.

**Event streaming platform.** Kafka and Kinesis handle the ingestion layer well but are not query engines. They always need a downstream storage database. Treating Kafka as "the database" is a common early mistake.

For the databases most commonly evaluated for fleet telemetry backends:

| **Database** | **SQL support** | **Geospatial** | **High-cardinality handling** | **Continuous aggregates** | **Managed service** |
| --- | --- | --- | --- | --- | --- |
| Tiger Cloud (TimescaleDB) | Full PostgreSQL SQL | PostGIS (native extension) | Hypertable + columnar compression | Yes, incremental | Yes - Tiger Cloud |
| InfluxDB 3.x | SQL + InfluxQL | Non-native | Columnar engine (v3 removes the tag-cardinality limits of earlier versions) | No (requires external aggregation job) | Yes - Cloud Serverless / Dedicated |
| QuestDB | SQL dialect | Geohash-based (limited vs. PostGIS) | Designed for high-cardinality; row-based storage | No | Limited |
| TDengine | SQL-like (TAOS) | Limited | Supertable model, optimized for many time series | Yes, but proprietary dialect | Yes |
| MongoDB | No SQL | GeoJSON + 2dsphere index | Document model; no time partitioning | No | Yes - Atlas |

InfluxDB's most-cited fleet deployment pairs it with Neo4J - one database for time-series telemetry, a second for vehicle relationship graphs. Tiger Data's answer to that pattern is a single PostgreSQL instance: [<u>hypertables</u>](https://www.tigerdata.com/learn/time-series-database-what-it-is-how-it-works-and-when-you-need-one) for telemetry, standard relational tables for vehicle metadata, and PostGIS for geospatial analytics, with no ETL between systems.

One practical note on InfluxDB: version fragmentation is a real operational risk. InfluxDB 1.x, 2.x, 3.0, Cloud Serverless, and Cloud Dedicated have meaningfully different APIs, retention policy models, and query languages. Teams inheriting an existing InfluxDB fleet setup should verify which version they're on before making architectural decisions.

For teams evaluating IoT database options more broadly, see the [<u>IoT database comparison</u>](https://www.tigerdata.com/learn/how-to-choose-an-iot-database) and [<u>IIoT database requirements</u>](https://www.tigerdata.com/learn/iiot-database-requirements) guides for a fuller picture.

## Schema design for fleet telemetry

Most database comparison articles describe options. This section shows the Data Definition Language (DDL).

### Narrow-row vs. wide-row models

The fundamental schema decision for vehicle telemetry is whether to model signals as rows or columns:

**Narrow-row model.** One row per `(time, vehicle_id, signal_name, value)`. Adding a new signal type requires no migration - just start inserting rows with a new `signal` value. High row volume, flexible schema. Recommended for heterogeneous fleets where vehicle types differ in their OBD-II signal sets.

**Wide-row model.** One row per `(time, vehicle_id)` with a column for each signal (`speed, rpm, fuel_level, lat, lon, ...`). Fewer rows, easier to query for known signal sets. Schema migrations required when signals are added. Better suited for uniform fleets with a stable, fixed signal set.

For most new builds, start with the narrow-row model. The flexibility to add signals without a migration is worth the higher row count.

### Hypertable DDL (TimescaleDB / Tiger Cloud) 

The examples below use TimescaleDB syntax, which powers Tiger Cloud. 

`-- Vehicle metadata (standard relational table)
CREATE TABLE vehicles (
    vehicle_id  TEXT PRIMARY KEY,
    make        TEXT,
    model       TEXT,
    year        INT,
    vin         TEXT UNIQUE,
    fuel_type   TEXT,   -- 'ICE', 'BEV', 'PHEV', 'HEV'
    fleet_group TEXT
);

-- Telemetry fact table (time-series, Hypercore columnstore enabled)
CREATE TABLE vehicle_telemetry (
    time        TIMESTAMPTZ      NOT NULL,
    vehicle_id  TEXT             NOT NULL REFERENCES vehicles(vehicle_id),
    signal      TEXT             NOT NULL,  -- 'speed_mph', 'rpm', 'fuel_pct', 'soc_pct', etc.
    value       DOUBLE PRECISION NOT NULL
) WITH (
    tsdb.hypertable,
    tsdb.segmentby = 'vehicle_id, signal',
    tsdb.orderby   = 'time DESC'
);`

The `tsdb.segmentby = 'vehicle_id, signal'` setting groups each vehicle's readings for each signal into the same columnstore segment. Queries filtering by `vehicle_id` can skip entire segments that don't match, keeping per-vehicle queries fast as the table grows. The columnstore policy is created automatically - older chunks are compressed to the columnstore in the background, typically achieving 90–98% storage savings.

### GPS location table

GPS data deserves its own table because the PostGIS `GEOMETRY` column type enables spatial indexing that a plain `DOUBLE PRECISION` lat/lon pair does not:

`-- GPS trajectory table
CREATE TABLE vehicle_location (
    time        TIMESTAMPTZ     NOT NULL,
    vehicle_id  TEXT            NOT NULL,
    position    GEOMETRY(Point, 4326)  -- WGS84 (standard GPS)
);

SELECT create_hypertable('vehicle_location', by_range('time'));

CREATE INDEX ON vehicle_location USING GIST (position);`

### Continuous aggregate for daily driver behavior

Pre-computing driver behavior metrics avoids rescanning the raw telemetry table on every dashboard load:

`CREATE MATERIALIZED VIEW vehicle_daily_summary
WITH (timescaledb.continuous) AS
SELECT
    time_bucket('1 day', time) AS day,
    vehicle_id,
    MAX(value) FILTER (WHERE signal = 'speed_mph')    AS max_speed_mph,
    AVG(value) FILTER (WHERE signal = 'speed_mph')    AS avg_speed_mph,
    SUM(value) FILTER (WHERE signal = 'idle_seconds') AS total_idle_seconds
FROM vehicle_telemetry
GROUP BY day, vehicle_id
WITH NO DATA;

SELECT add_continuous_aggregate_policy('vehicle_daily_summary',
    start_offset => INTERVAL '3 days',
    end_offset   => INTERVAL '1 hour',
    schedule_interval => INTERVAL '1 hour'
);`

The [<u>continuous aggregate</u>](https://docs.timescale.com/use-timescale/latest/continuous-aggregates/about-continuous-aggregates/) refreshes incrementally - it only reprocesses chunks that changed since the last refresh, not the entire history. For a 1,000-vehicle fleet, the daily rollup query that previously rescanned ~950 million rows now reads from a pre-computed summary table.

For the full continuous aggregates documentation, see [<u>docs.timescale.com/use-timescale/latest/continuous-aggregates/</u>](https://docs.timescale.com/use-timescale/latest/continuous-aggregates/).

## Geospatial queries on fleet telemetry

Fleet analytics regularly require spatial queries: "show me all vehicles within 5 km of this depot" or "flag vehicles that entered a restricted geofence." These need spatial indexing - a lat/lon column pair doesn't give you that.

PostGIS runs inside the same PostgreSQL instance as the telemetry hypertables. Here's a geofence detection query:

`-- Find vehicles that entered a geofence in the last hour
SELECT DISTINCT vehicle_id
FROM vehicle_location
WHERE time > NOW() - INTERVAL '1 hour'
  AND ST_Within(
      position,
      ST_Buffer(
          ST_SetSRID(ST_MakePoint(-87.6298, 41.8781), 4326),
          0.05  -- ~5 km radius in degrees (approximate)
      )
  );`

Because the `vehicle_location` hypertable is in the same PostgreSQL instance as the `vehicles` metadata table, you can join geospatial results to vehicle ownership data, fleet group assignments, or driver records in a single query. No ETL, no separate geo database, no JOIN across network boundaries.

For the full PostGIS feature set with Tiger Data, see the [<u>PostGIS with Tiger Data for geospatial time-series queries</u>](https://www.tigerdata.com/learn/postgresql-extensions-postgis) guide.

## Edge-to-cloud architecture

Understanding where data originates helps you design the ingestion path correctly.

Each vehicle runs a **Telematics Control Unit (TCU)** - a device that reads OBD-II or CAN bus data, combines it with GPS, buffers locally, and transmits to the central backend. Common protocols are MQTT over cellular, HTTP REST, or proprietary binary formats.

Three ingestion paths to Tiger Cloud are common in production:

**MQTT pipeline.** The TCU publishes to an MQTT broker (Mosquitto, HiveMQ, EMQX). Telegraf or a custom consumer subscribes and writes to Tiger Cloud. Best fit for fleets with existing MQTT infrastructure or constrained bandwidth where connection overhead matters. See the [<u>MQTT to PostgreSQL ingestion pipeline</u>](https://www.tigerdata.com/learn/mqtt-to-postgresql) guide for implementation details.

**Direct HTTP/REST.** The TCU sends batched payloads directly to an application layer (the fleet platform API), which writes to Tiger Cloud. Lower per-message protocol overhead than MQTT. Suitable when the fleet platform owns the ingestion layer.

**Kafka / Kinesis / Pub/Sub.** An event streaming bus as the ingestion layer, with Tiger Cloud as the downstream storage database. The open-source `fleet-telemetry` project ([<u>github.com/teslamotors/fleet-telemetry</u>](https://github.com/teslamotors/fleet-telemetry)) supports Kafka, Kinesis, and Pub/Sub as dispatch backends but explicitly leaves storage to the implementer. Tiger Cloud is a compatible storage target via any of those backends.

One architectural requirement that's easy to miss: TCUs buffer data locally when cellular connectivity drops (tunnels, rural routes, loading docks). When connectivity resumes, they flush, which means out-of-order writes are normal. The hypertable approach handles late-arriving data correctly because each row carries its own timestamp. There's no assumption that insert order equals event order.

For teams managing local storage on the TCU itself, see the [<u>edge database sync patterns</u>](https://www.tigerdata.com/learn/edge-database) guide.

## EV fleet telemetry: battery-specific workload

EV fleets add battery management system (BMS) data on top of the base GPS and OBD-II signals. The write pattern is different: BMS data streams at higher frequency during fast-charging (sub-second updates are possible during DC fast charge) and lower frequency when the vehicle is idle.

The BMS signals that differ from ICE OBD-II:

- **State of charge (SoC)** - percentage of usable capacity remaining. Changes continuously during driving and charging.
- **State of health (SoH)** - long-term capacity degradation. Changes slowly but requires years of high-frequency history to compute accurately.
- **Cell temperature by module** - lithium packs have dozens to hundreds of cells; thermal management systems monitor temperature per module or cell group.
- **Charge session events** - start time, stop time, energy delivered, peak charging rate. These are discrete events, not continuous telemetry.

In the narrow-row schema above, all of these are just additional `signal` values: `soc_pct`, `soh_pct`, `cell_temp_c_module_3`, etc. No schema migration is required to add them.

For teams building OCPP-based charging networks alongside fleet tracking, see the [<u>EV charging management system database architecture</u>](https://www.tigerdata.com/learn/ev-charging-station-data-management) guide.

## Choosing a database for fleet telemetry

No single database is right for every fleet. Here's a decision framework based on the criteria that actually differentiate options at fleet scale.

**Choose Tiger Cloud if:**

- Your fleet platform already runs on PostgreSQL, or you want one database for telemetry, relational vehicle metadata, and geospatial queries without ETL between them
- You need SQL joins between telemetry and vehicle master data
- You need geofencing or trajectory analysis without a separate spatial database (PostGIS runs natively in the same instance)
- You want continuous aggregates for driver behavior metrics without a scheduled batch job
- You want managed infrastructure (Tiger Cloud) with the option to self-host on TimescaleDB (the open-source project) if needed

**Choose InfluxDB if:**

- Your team is already invested in the InfluxDB ecosystem (Telegraf, Flux, Chronograf) and migration cost outweighs the architectural benefits of SQL
- You are on InfluxDB 3.x Cloud Dedicated and the workload is pure time-series with no relational joins required
- Note: InfluxDB version fragmentation (1.x vs. 2.x vs. 3.0 APIs) is a real operational risk for teams inheriting existing deployments

**Choose QuestDB if:**

- The primary workload is bulk ingestion and fast time-range queries with no geospatial or join requirements
- You are comfortable with a smaller ecosystem and fewer managed service options

**Choose TDengine if:**

- The fleet is very large (millions of vehicles), the team is comfortable with a non-SQL query dialect, and the Chinese enterprise support model fits the organization

**Don't use Tiger Data if:**

- The primary workload is graph-based - complex vehicle-to-vehicle relationship queries or route optimization graphs. A dedicated graph database handles that pattern better.
- The team's existing stack is deeply invested in InfluxDB 1.x with no migration budget. At small fleet sizes, the switching cost may not justify the architectural change.

For broader IoT workload context, the [<u>time-series databases compared</u>](https://www.tigerdata.com/learn/the-best-time-series-databases-compared) page covers more options across more dimensions.

Ready to build your fleet telemetry backend? [<u>Start a Tiger Cloud trial</u>](https://www.tigerdata.com/cloud/) or explore the [<u>TimescaleDB documentation</u>](https://docs.timescale.com/) for self-hosted deployment.