Second product

Every message flespi normalises, in storage you own

flespi is very good at the part nobody wants to write: 160 device protocols, decoded and normalised into one message shape. What it is not is your archive. This mirrors every message it produces into a self-hosted ClickHouse cluster — same idea as FleetSQL, different platform, different store.

Reads
flespi channels, MQTT 5 + REST
Lands in
ClickHouse you host yourself
Durability
fsynced to disk before ack
Measured
~1,470 devices, ~1,400 msg/min peak

Why mirror flespi at all

Two reasons, and they are both about the storage rather than the ingestion. flespi's own retention is priced per message-day, so keeping several years of history there costs more than keeping it yourself. And it is cloud-only: if a customer needs their telematics data inside a jurisdiction you control, no plan tier answers that.

So the split is deliberate. flespi stays the ingestion and normalisation layer — the thing it is genuinely best at — and the long-term store moves to a machine you choose, in a country you choose, under a retention policy you set rather than one you rent.

  • Retention becomes a disk decision. Hot data in ClickHouse, older parts tiered to object storage, both under one table.
  • Residency becomes an address. One VM or EC2 instance in the region the contract names. No Kubernetes in the picture.
  • Analytics stop competing with ingestion. A heavy aggregate runs against your cluster, not against the platform your devices are writing to.

Nothing is acknowledged before it is on your disk

Messages arrive over MQTT 5 with QoS 1 and manual acknowledgement, on a channel-level subscription — one subscription covers every device on the channel, so no billable device entities are created to read them. Each message is written and fsynced to a local disk spool before it is acked, which moves the durability boundary from a broker buffer you cannot inspect onto hardware you can.

A batcher drains that spool into ClickHouse in batches of 20,000 rows or two seconds, whichever comes first. Identity is (org_id, ident, ts, ts_key) over a ReplacingMergeTree with insert deduplication tokens, which turns at-least-once delivery into exactly-once once the parts merge. A ClickHouse outage therefore cannot open a gap on the flespi side: the spool keeps acknowledging and the backlog drains when the cluster returns.

Live

flespi channel

MQTT 5 · QoS 1

Disk spool

fsync, then ack

Batcher

20k rows / 2s

ClickHouse

17 columns + JSON

Healing

Reconciler

watches freshness

REST backfill

the missing range

Gap ledger

Postgres, what it could not heal

Read

Console

map, message browser, CSV

Grafana

read-only account

Your queries

ClickHouse SQL

Two paths into one table: MQTT carries the live stream, REST closes whatever the stream missed, and both land on the same identity key so a re-pull replaces rather than duplicates.

Gaps heal themselves, and the ones that cannot are written down

There are no webhooks in this design, which makes backfill the only thing standing between an outage and permanent loss — so it is built as a first-class component rather than a script somebody runs. A reconciler watches per-channel freshness against what is actually in ClickHouse and, when a channel falls behind, pulls the missing time range over the REST API into the same identity key. Re-pulling a window replaces it instead of duplicating it.

Healing has a limit and the limit is stated rather than hidden: a consumer can be down for as long as flespi's per-channel buffer holds, which at real volumes is hours rather than the nominal thirty days. Anything past that is recorded in a Postgres ledger as lost, alerted on, and covered by a runbook — which is a worse outcome than healing it, and a much better one than a silent hole in a chart nobody notices for a quarter.

A schema that absorbs parameters nobody planned for

flespi's parameter catalogue is the union across every protocol it supports: 4,188 entries at the last regeneration. Any one device emits a few dozen of them, and which few dozen depends on the model. A table with a column per parameter would be both enormous and permanently out of date.

Instead each row carries 17 typed hot columns — the fields every query touches, with their own codecs — plus a native JSON column holding the sparse tail. A device that starts emitting parameter 4,189 next week lands with no migration and no deploy. The catalogue ships embedded in the binary and is served to the console, so a parameter nobody hardcoded still shows up with a human label and a unit.

FleetSQL flespi backup
SourceWialon Hosting, over its APIflespi channels, MQTT 5 + REST
StorePostgreSQL, one database per clientClickHouse, self-hosted
ShapeOne table per data source, fixed columns17 typed columns plus a JSON tail
FreshnessFrom every 15 minutes to nightlyContinuous, seconds behind the device
HostingManaged for you, EU-hostedYour VM, your region, your retention
Good forReporting, BI, joining with your ERPHigh-volume archive and raw message history

Multi-tenant, because an integrator is never one fleet

Each organisation gets its own MQTT runner under a supervisor that reconciles what should be running against what is, and its own flespi token pair, encrypted with a versioned AES-GCM keyring so rotation is a version bump rather than a migration. The fleet hierarchy lives in Postgres and is mirrored into ClickHouse dictionaries, which is what lets a query filter by fleet without joining across two stores.

One binary serves both the JSON API and the React console: a fleet map, a message browser over any promoted column or catalogue parameter with an AND/OR filter tree and CSV export, device discovery, and per-user access granted fleet by fleet. Every route is session-authenticated and scope-checked per organisation.

  • Grafana gets its own account. A read-only ClickHouse user for dashboards, separate from the ingest account and from the admin API's.
  • Prometheus and Alertmanager ship with it. Thirty-one rules, including a watchdog whose absence is the alert.
  • Backups are drilled, not assumed. Nightly to object storage, with a restore that has been executed and written up.

Where it is today

Running against a real flespi account: 8 channels, about 1,470 devices, measured at roughly 1,400 messages a minute in the peak hour and 320 averaged across the day. That 4.4× diurnal swing is the number that matters when sizing — the peak is what plan ceilings have to clear, the average is what the storage bill derives from.

It is deliberately not an FMS. There are no dashboards for drivers, no dispatch screen and no mobile app; it is a mirror with an operator console, and the analytics happen in whatever already reads ClickHouse. If you want the managed, reporting-shaped version of this idea against Wialon instead, that is FleetSQL.

Frequently asked questions

How is this different from FleetSQL? +

Same idea, different platform and different store. FleetSQL reads Wialon through its API on a schedule and lands reporting-shaped tables in a managed PostgreSQL database. This consumes flespi's message stream continuously and lands raw normalised messages in a ClickHouse cluster you host. If your devices are on Wialon, you want the first; if they are on flespi, the second.

Why ClickHouse rather than PostgreSQL? +

Volume and shape. A flespi mirror is an append-only firehose of wide, sparse messages measured in millions per day, and the questions asked of it are aggregates over time ranges. That is the workload columnar storage was built for, and the one where a row store starts costing more machine than the answers are worth.

What happens if the consumer is down for an hour? +

Nothing is lost. flespi buffers the channel while the subscription is offline, and on reconnect the reconciler notices the freshness gap and closes it by REST backfill into the same identity key, so no row is duplicated. The limit is the per-channel buffer window, which at real volumes is hours rather than the nominal thirty days — past that the gap is recorded as lost and alerted rather than silently absorbed.

Do you use webhooks? +

No, and that is a decision rather than an omission. flespi offers four access methods; this uses MQTT for real-time and REST for history, the pairing flespi itself recommends. Webhooks would add a second delivery path with its own failure modes and no better durability, since the disk spool is what makes delivery safe here.

Who holds the data, and where? +

You do, on infrastructure you choose — a VM or an EC2 instance in whichever region the contract names, with no Kubernetes required. Per-organisation flespi tokens are encrypted at rest with a versioned AES-GCM keyring, every API route is scoped by organisation, and retention is a policy you set rather than a plan tier you rent.

Can we query it from Grafana or a BI tool? +

Yes. ClickHouse has a first-party Grafana data source and drivers for everything else, and the deployment ships with a dedicated read-only account for dashboards so reporting never shares credentials with ingestion. The console's message browser also exports CSV for the one-off questions that do not deserve a dashboard.

Contact

Interested in FleetSQL?

Why FleetSQL

Your telematics data in a PostgreSQL database you control — queryable, joinable, and yours.

Managed, not installed
We run the extraction and the database. You get a connection string.
One database per client
Your own PostgreSQL, hosted in the EU, TLS required.
Your tools, unchanged
Power BI, Grafana, Metabase, psql, pandas — anything that speaks Postgres.
An evaluation first
A trial database on your own fleet before anything is signed.

Or write to us directly: [email protected]