Hey everyone,
I’ve been working on something I want to share and get feedback on.
The itch
Every web app I build ends up with the same stack: a database, a cache layer, and the app itself. Three things to deploy, monitor, pay for, and hope they stay connected to each other.
For caching, the options are:
Redis — great protocol, great ecosystem, but in production you need a managed service (ElastiCache, Upstash, etc.) because self-hosted Redis persistence is… optimistic. AOF rewrite can lose your last few seconds. RDB snapshots lose everything since the last dump. And now you’re paying for a whole separate instance just to hold keys.
Java solutions* (Hazelcast, Infinispan) — powerful but painful to operate. Config files longer than your app code. JVM tuning. Cluster formation issues. Not exactly “easy to maintain.”
ETS/Mnesia — built into the BEAM, fast, but volatile. Node goes down, data’s gone. Mnesia replication exists but comes with its own set of surprises.
What I actually wanted: use the disk I’m already paying for on my instance, speak a protocol I already know (Redis), get real durability without a separate service, and keep my stack simple.
What I built with AI
[FerricStore]( GitHub - yoavgeva/ferricstore: Distributed crash-safe key-value store with Redis wire protocol. Durable by default — every write is Raft-committed and fsync'd. Embeddable Elixir library or standalone server. Built with Elixir + Rust NIFs. · GitHub ) is a persistent key-value store written in Elixir + Rust that:
Speaks RESP3 — connect with redis-cli, or any Redis client library that support RESP3. Your existing code mostly just works.
Every write is durable by default — Raft consensus + Bitcask append-only log + fsync. When you get OK back, your data is on disk. Not “eventually.” Not “if the AOF rewrite finishes.” On disk, right now.
Runs embedded in your app — add it as a dependency, FerricStore.set("key", "value"). No separate process, no network hop, no connection pool. Your cache lives in the same BEAM node as your app.
Or runs standalone — `docker run -p 6379:6379 yoavgeva/ferricstore` and connect with redis-cli. Drop-in for apps that already speak Redis.
50+ Redis commands — strings, hashes, lists, sets, sorted sets, TTL, MULTI/EXEC, pub/sub, and more.
Native Elixir commands — CAS (compare-and-swap), distributed locks, rate limiting, FETCH_OR_COMPUTE — things you’d build on top of Redis but get out of the box here.
Probabilistic data structures — Bloom filters, Cuckoo filters, Count-Min Sketch, TopK, HyperLogLog, T-Digest. All built-in.
Vector search — HNSW index for similarity search, built into the storage engine.
The architecture in 30 seconds
Client (redis-cli / Redix / your app)
|
v
RESP3 Parser (pure Elixir, zero-copy)
|
v
Command Dispatcher
|
v
Raft Consensus (via ra library — same one RabbitMQ uses)
|
v
Bitcask Storage Engine (Rust NIF — append-only log, CRC-checked)
|
v
ETS Hot Cache (recent values served from memory, cold values read from disk)
Writes go through Raft for consistency, then to Bitcask for persistence. Reads hit ETS first (microseconds), fall back to disk for cold data. The Rust NIF handles the low-level I/O — pure functions, no dirty schedulers, proper consume_timeslice yielding so the BEAM scheduler stays happy.
Embedded mode — the thing I’m most excited about
# mix.exs
{:ferricstore, "\~> 0.1"}
# your app
FerricStore.set("user:123:session", session_data)
{:ok, data} = FerricStore.get("user:123:session")
# with TTL
FerricStore.set("rate:api:123", "1", ttl: :timer.seconds(60))
# atomic operations
{:ok, new_count} = FerricStore.incr("page_views")
# compare-and-swap
:ok = FerricStore.cas("inventory:sku42", "10", "9")
No Redis connection. No connection pool config. No “what happens when Redis is down.” It’s just a function call that persists to disk. Your Phoenix app, your cache, one deployment, one thing to monitor.
Standalone mode — drop-in Redis replacement
If you have apps in other languages, or you just want a Redis-compatible server with real durability:
bash
# Docker
docker run -p 6379:6379 -v ferricstore_data:/data yoavgeva/ferricstore
# Then use any Redis client
redis-cli SET mykey "hello"
redis-cli GET mykey
Or from your Elixir app via Redix:
elixir
{:ok, conn} = Redix.start_link("redis://localhost:6379")
Redix.command!(conn, \["SET", "user:42", "alice"\])
Redix.command!(conn, \["GET", "user:42"\])
# => "Alice"
Everything you’d expect from Redis works — MULTI/EXEC transactions, pub/sub, pipelining, HELLO 3 (RESP3). Plus you get a built-in health endpoint (GET /health on port 6380), Prometheus metrics, ACL authentication, and TLS support.
The dashboard gives you live visibility into shards, key counts, memory pressure, hit rates, and slow queries — no Grafana setup needed.
What it’s NOT
Not a database replacement — it’s a cache/store. Great for sessions, rate limits, feature flags, job queues, counters, leaderboards. Not for your users table.
Not a sharded cluster (yet) — scales to 3-5 nodes via Raft replication for high availability and read scaling (every node serves reads from local ETS). Storage capacity scales with disk, not RAM — a 500GB NVMe gives you 500GB of cache. But there’s no hash-slot sharding across nodes yet, so every node holds all the data.
Not battle-tested in production — this is v0.1. It passes 8000+ tests including shard-kill recovery and multi-node cluster tests, but it hasn’t seen real production traffic yet. That’s where you come in.
Not going to beat Redis on raw throughput — Redis keeps everything in RAM and doesn’t fsync by default. But it’s persistent store where every write hits disk,
Why Elixir + Rust?
Elixir gives us the BEAM’s supervision trees, distribution primitives, and ETS for the hot cache. Rust gives us a memory-safe, zero-copy storage engine that doesn’t GC-pause or mess with the BEAM scheduler.
The NIF boundary is clean — Rust functions are pure and stateless. No Mutex, no shared state, no dirty schedulers. Just `v2_append_batch(path, entries)` and `v2_pread_at(path, offset)`.
Looking for
Feedback on the API, the architecture, the docs — what’s confusing, what’s missing, what would make you try it?
Early adopters willing to try it in a side project or staging environment and report what breaks.
Use case ideas — what would you use a durable, embedded Redis-compatible cache for in your Elixir apps?
Links:
- Hex: ferricstore | Hex
- Docs: ferricstore v0.1.0 — Documentation
- Docker: yoavgeva/ferricstore - Docker Image
- GitHub:
Happy to answer any questions. And yes, the name is a pun — Ferric (iron/Rust) + Store.






















