v1.0.0 · Open source · Apache 2.0 · linux/amd64 & arm64

A time series database
that starts as a cluster.

Vortex TSDB is a lightweight, distributed time series database for real-time metrics. Write samples over HTTP; read per-minute aggregates, instant values, and tumbling or sliding windows. Every node holds a full replica in memory.

  terminal
$ docker compose up -d                              # 3 nodes + gateway + console
$ curl -X POST 'localhost:9080/tsd/push?t=long&c=car&d=speed&v=44'
$ curl 'localhost:9080/tsd/query?t=long&c=car&d=speed&range=6h&step=5&window=15'
{ "step": 5, "window": 15, "last": { "value": 44 }, "points": [ { "count": 900, "highestValue": 131, "averageValue": 69.8 }, … ] }
Vortex TSDB web console: live values and trends of one category

Why Vortex?

Live metrics need exact counts, fast windows and a node that can die. They rarely need a storage engine and a query language.

Several components to deploy and operate

One service, one command

A Spring Boot service and an optional Redis. ./run-docker.sh starts a whole cluster.

Counts drift under concurrent writers

Exact without locks

Every write is applied on the leader, one at a time, then replicated as an operation.

Windows need tables or a query language

Four HTTP parameters

range, step, window, z. Windows are folded from minute buckets at read time.

A node goes down, so does the service

Full replica on every node

A new leader within 5 s after a crash; a graceful stop hands over at once.

Memory grows with every series

Bounded memory

A per-node key limit; cold keys move to Redis and are read back on demand.

Features

Everything a real-time TSDB needs, except a query language.

Per-minute aggregates

Count, highest, lowest, total and average for long, double and decimal.

Tumbling & sliding windows

Any range up to the retention, any step, any window, aligned in the zone you ask for.

Instant values

The latest sample of every series; a whole category in one read.

Replicated cluster

Nodes find each other and elect a leader. Reads come from local memory on any node.

Snapshots

Written at shutdown and every 5 minutes. Restarts keep the data; expired buckets are dropped on load.

Redis overflow

Keys beyond VORTEX_CACHE_MAX_KEYS spill to Redis when one is configured.

Cluster health API

Per node QPS, error rate, cache, replication lag, leader and JVM at /tsd/health.

Gateway & OpenAPI

Traefik load-balances healthy nodes; Swagger UI documents every endpoint.

Legacy Vortex compatible

/tsd/push, /tsd/test and /tsd/retrieve keep their parameters and responses.

How it works

Writes are serialised on the leader and replicated as operations; reads fold minute buckets from local memory.

  1. Write: any node forwards a sample to the leader, which applies max, min, sum(+count) and the latest value to the minute bucket.
  2. Replicate: each operation is broadcast to every node, so every node holds the whole dataset.
  3. Read: the node that receives a query folds the buckets it covers from its own memory.

Quick start

Now on Docker Hub, free to pull. Ready-made images for amd64 and arm64: no JDK, Node or build needed, and latest always carries the newest build. Also on GHCR.

  bash
curl -fsSLO https://raw.githubusercontent.com/paganini2008/vortex/main/deploy/docker-compose.yml
docker compose up -d
curl -X POST 'http://localhost:9080/tsd/push?t=long&c=car&d=speed&v=44'
curl 'http://localhost:9080/tsd/last?t=long&c=car&d=speed'
Entry pointURL
HTTP APIhttp://localhost:9080/tsd/…
Swagger UIhttp://localhost:9080/swagger-ui.html
Web consolehttp://localhost:9080/
Redis overflow (optional)VORTEX_REDIS_HOST=… VORTEX_REDIS_PASSWORD=… docker compose up -d
From GHCRdocker pull ghcr.io/paganini2008/vortex-tsdb:latest · or VORTEX_REGISTRY=ghcr.io/paganini2008 docker compose up -d
From sourcegit clone …/vortex.git && ./run-docker.sh

Examples

Every response is wrapped as {"code": 1, "msg": "ok", "data": …}; code is 0 on failure.

Three writes land in the same minute bucket; the instant value is the last one.

for v in 44 67 112; do curl -X POST "localhost:9080/tsd/push?t=long&c=car&d=speed&v=$v"; done
curl 'localhost:9080/tsd/last?t=long&c=car&d=speed'
→ {"value": 112, "timestamp": 1791090941234}
curl 'localhost:9080/tsd/retrieve?t=long&c=car&d=speed&z=Asia/Shanghai'
→ "09:29:00": {"count": 3, "highestValue": 112, "lowestValue": 44, "totalValue": 223, "averageValue": 74.3333}
Query explorer: a series queried with a range, step and window, as chart and table
Query explorer in the bundled web console: any series, range, step and window, with the matching API call.

Performance

Writes are serialised on the leader: throughput is bounded by one node, consistency is exact.

0failures in 826,781 writes, identical totals on every node
~1,000/ssamples where throughput levels off
1,500/speak samples, leader CPU ~230%, heap ≤ 310 MB
4.7 sto a new leader after kill -9
Vortex TSDBPrometheusInfluxDBTimescaleDB
WritesHTTP pushPeriodic pullHTTP pushSQL
QueriesHTTP parametersPromQLInfluxQL / SQLSQL
StorageIn-memory replicas + snapshotsLocal diskDiskPostgreSQL
High availabilityBuilt inExtra setupBy editionPostgreSQL tooling
Best forReal-time metrics, days of retentionMonitoring & alertingGeneral time seriesLong-term analytics

Measured on Docker Desktop (4 CPUs, 8 GB), 3 nodes, k6 through the Traefik gateway. Leader killed at 600 samples/s: 0.28% of requests failed, all at the moment of the kill.