The key-value store that moves like the tide.
marekvs speaks the Redis protocol, but underneath it's an AP, coordination-free database: no leader, no quorum, no in-memory dataset. Writes are fire-and-forget and converge through hybrid logical clocks and CRDT merges. Any node serves any key.
FROM scratch image
Kubernetes operator
$ redis-cli -p 6379
marekvs> SET greeting hello
OK
marekvs> SADD tags rust distributed redis
(integer) 3
marekvs> INCR visits
(integer) 1
# … and every node already has it.
Built for movement, not coordination
Redis protocol
RESP2 & RESP3 over the same :6379 your clients already
use. Strings, hashes, sets, sorted sets, lists, streams, pub/sub, HyperLogLog.
Convergent replication
Hybrid logical clocks + ORSWOT / PN-counter merges. Concurrent
SADDs on different nodes both survive; deletes never resurrect.
Dynamic replication
Any node serves any key. Read a remote key and the node caches it and subscribes to its updates — replication follows demand.
Bounded staleness
Sequence-cursor catch-up plus Merkle anti-entropy repair divergence within seconds. Milliseconds typical, 15 s worst case.
Kubernetes-native
Gossip membership, DNS-seeded discovery, a StatefulSet, and a
MarekvsCluster operator with safe scaling and autoscaling.
Lua scripting
EVAL / EVALSHA with Redis-grade atomicity
when keys co-locate via hash tags. Script effects replicate, never the script.
What we promise — and what we don't
marekvs is AP: available and partition-tolerant, eventually consistent. These are the guarantees the docs stand behind.
| Guarantee | What it means |
|---|---|
| Read-your-writes | A connection always sees its own prior writes. |
| Monotonic reads | A connection's reads never go backward in time. |
| Convergence | Given no new writes, all replicas reach the same value. |
| No resurrection | A deleted key stays deleted; tombstones outlive repair. |
| Exact counters | Concurrent INCR/DECR across nodes are never lost. |
| Bounded staleness | Cross-node divergence heals within seconds (15 s worst case). |
Not linearizable, not CP, no cross-node transactions, no
Redis Cluster MOVED/ASK redirects. Two clients on
two nodes can briefly read different values. See
Consistency.
System shape at a glance
Redis clients (redis-cli, any RESP driver)
│ :6379
┌─────────────────┴─────────────────┐
│ Kubernetes Service │
└──┬──────────────┬──────────────┬───┘
│ │ │
┌────┴───┐ ┌────┴───┐ ┌────┴───┐
│ pod 0 │ │ pod 1 │ │ pod 2 │
│ RESP │ │ RESP │ │ RESP │
│ engine │ │ engine │ │ engine │
│ ondaDB │ ⇄ │ ondaDB │ ⇄ │ ondaDB │ :7373 mesh
└────────┘ └────────┘ └────────┘
└──────── chitchat gossip ─────────┘ :7946/udp
Five subsystems per node — RESP frontend, command engine, disk-native storage (ondaDB LSM), replication engine, and the gossip cluster layer. Read the architecture →
Honest about performance
marekvs is disk-native and pays a convergent-merge tax, so it trades raw throughput for durability and coordination-free scale. In the KeyDB comparison harness it lands around 0.4–0.6× of an in-RAM store — by design, with the full methodology in Performance.
Up in one command
docker run -d --name marekvs -p 6379:6379 \
-e MAREKVS_NODE_ID=0 -e MAREKVS_REPLICAS_N=1 \
ghcr.io/yannick/marekvs:latest
redis-cli -p 6379 ping # PONG
- A Redis-compatible endpoint on
:6379 - Health + Prometheus metrics on
:9121 - Scale to 3 nodes with one env var — see the guide
Ready to dive in?
Start with the overview, or jump straight to the API reference.