Skip to content

Fleet telematics to Iceberg

Location pings from two fleets into one PicoMQ stream per fleet, then one Iceberg table.

Source: examples/connectors/fleet-telematics-iceberg.

seed.shpico append --batch 20fleets.afleets.biceberg sinkpattern fleets\..*iceberg-rest :8181catalogRustFS :9000s3://lake/warehousecreatetablenamespace, tableduckdbATTACH iceberg, httpfsPostgres :5432pico metadataRustFS s3://picomqpico WAL and objectsconnectors :8081state volumepico :9092snapshotparquetSELECTalso in the stack

Run

bash
cd examples/connectors/fleet-telematics-iceberg
docker compose up -d --build
./seed.sh
./verify.sh
ServiceHost
PicoMQhttp://localhost:9090
Connectors runtimehttp://localhost:8081
Iceberg REST cataloghttp://localhost:8181
RustFShttp://localhost:9001
DuckDBdocker compose exec duckdb duckdb -init /opt/duckdb.sql

--build compiles the Iceberg sink image. It is not in the default connectors image.

Sink

iceberg-sink.toml:

toml
[[topics]]
pattern = "fleets\\..*"

[plugin_config]
tables = ["analytics.locations"]
catalog_type = "rest"
uri = "http://iceberg-rest:8181"
warehouse = "s3://lake/warehouse/"
store_url = "http://rustfs:9000"
store_path_style_access = true

The table is not created by the sink. createtable in compose.yml posts sql/namespace.json and sql/table.json to the catalog first.

tablesResult
analytics.locationsOne table, fleet_id as a column
analytics.locations_{topic_segment[-1]}analytics.locations_a, analytics.locations_b

Verify

PicoMQIceberg (DuckDB)
Fleetspico ls --prefix /fleets.SELECT fleet_id, count(*) FROM lake.analytics.locations GROUP BY 1
Fleet Apico read /fleets.aSELECT * FROM lake.analytics.locations WHERE fleet_id = 'a'

Resume

bash
docker compose kill connectors
printf '%s\n' '{"id":"a-resume","fleet_id":"a","vehicle_id":"van-1","lat":51.51,"lon":-0.12,"ts":"2026-09-05T21:00:00Z"}' \
  | docker compose exec -T pico pico append /fleets.a --batch 1
docker compose start connectors
./verify.sh