tiny-redis is a Redis-compatible cache server written in Go. It implements the RESP protocol, stores data in memory, persists Raft state with BoltDB, and supports log-based replication between multiple nodes.
- RESP protocol compatibility – works with
redis-cli, Medis, AnotherRedisDesktopManager, etc. - In-memory data structures covering strings, hashes, lists, sets, sorted sets, TTL, and more.
- Raft-based clustering with automatic leader election and log replication.
- Durable metadata: Raft log, stable store, and snapshots persisted to disk.
- Pluggable logging using Go 1.21+
log/slogwith optional sampling and JSON file output. - CLI configuration via flags or config file.
- Go 1.25+
- macOS/Linux (tested) – Windows via WSL2
- Docker 24+ (optional)
git clone https://github.com/HSn0918/tinyredis
cd tinyredis
GOEXPERIMENT=greenteagc go build ./cmd/tinyredis
# start a single Raft node on 127.0.0.1:6379
./tinyredis node \
--host 127.0.0.1 \
--port 6379 \
--node-id node-1 \
--raft-dir ./data/node1 \
--raft-bind 127.0.0.1:7000 \
--raft-http 127.0.0.1:17000 \
--raft-bootstrapThen connect with redis-cli:
redis-cli -p 6379 pingData lives in memory, while Raft metadata is written to ./data/node1/.
If you don’t need clustering or replication, you can run in standalone mode:
./tinyredis node \
--host 127.0.0.1 \
--port 6379 \
--standaloneIn standalone mode, all writes apply directly to the in-memory database and there is no leader election or log replication.
-
Bootstrap the first node
./tinyredis node --node-id node-1 \ --host 127.0.0.1 --port 6379 \ --raft-dir ./data/node1 \ --raft-bind 127.0.0.1:7000 \ --raft-http 127.0.0.1:17000 \ --raft-bootstrap
Wait until logs show it as leader.
-
Join additional nodes Each new node needs an empty raft directory and
--raft-joinpointing to the leader’s HTTP address:./tinyredis node --node-id node-2 \ --host 127.0.0.1 --port 6380 \ --raft-dir ./data/node2 \ --raft-bind 127.0.0.1:7001 \ --raft-http 127.0.0.1:17001 \ --raft-join 127.0.0.1:17000
Repeat for node-3, etc.
-
Restarting existing nodes
- Keep their
--node-id,--raft-dir,--raft-bind,--raft-http. - Do not pass
--raft-joinor--raft-bootstrapagain. - Start the former leader first, then the followers.
- Keep their
-
Adding a brand new node
- Create a fresh directory for its raft state.
- Start with
--raft-joinpointed at the current leader.
Quick start:
make cluster-uplaunches a preconfigured three-node cluster (logs/PIDs under.devcluster);make cluster-downstops it.
| Flag | Description |
|---|---|
--host, --port |
RESP listening address. |
--node-id |
Unique Raft server ID (string). |
--raft-dir |
Directory for Raft log/stable/snapshots. |
--raft-bind |
TCP address for Raft transport (peer replication). |
--raft-http |
HTTP address for join requests. |
--raft-join |
leader-host:leader-http-port to join an existing cluster. |
--raft-bootstrap |
Bootstrap a new cluster when no state exists. |
--logdir |
Directory for redis.log (JSON). |
--loglevel |
debug, info, warn, error. |
--log-sampling |
Enable log sampling (default true). |
--log-sampling-interval |
Sampling window (default 1s). |
Run ./tinyredis node --help for the full list.
Build locally:
docker build -t tinyredis:latest .Run single node:
docker run -d --name tinyredis \
-p 6379:6379 \
-v $PWD/data/node1:/data \
tinyredis:latest \
./tinyredis node --host 0.0.0.0 --node-id node-1 \
--raft-dir /data --raft-bind 0.0.0.0:7000 \
--raft-http 0.0.0.0:17000 --raft-bootstrapFor multi-node clusters you need multiple containers (or compose) with different node IDs, ports, and persistent volumes.
- Start three nodes (see the
bootstrap/joinhelpers above) and inspect each node:Exactly one node reportsredis-cli -p 6379 INFO replication redis-cli -p 6380 INFO replication redis-cli -p 6381 INFO replication
role:master; the others reportrole:slaveand point to the sameleader_id. - Write a value against the leader:
redis-cli -p 6379 SET failover demo
- Press Ctrl+C in the leader’s terminal to stop it. After a few seconds, rerun
INFO replicationon the remaining nodes to see a new leader elected (role:master, updatedleader_id). - Read the value back from the new leader:
redis-cli -p 6380 GET failover
- When bringing the old node back, reuse its original configuration without
--raft-join/--raft-bootstrap. For example:You can also rely on the helper target:./bin/tinyredis node --host 127.0.0.1 --port 6379 \ --node-id node-1 \ --raft-dir ./data/node1 \ --raft-bind 127.0.0.1:7000 \ --raft-http 127.0.0.1:17000
make rejoin REJOIN_NODE=node-1 REJOIN_PORT=6379 \ REJOIN_RAFT=127.0.0.1:7000 REJOIN_HTTP=127.0.0.1:17000 \ REJOIN_HOST=127.0.0.1
INFO replicationnow includesrole,leader_id,leader_raft_addr, andknown_peers, making it easy to script health/leader checks.
Need a single stable RESP endpoint for tools like redis-cli? Run the built-in proxy:
./bin/tinyredis proxy \
--listen 127.0.0.1:7390 \
--nodes 127.0.0.1:6379,127.0.0.1:6380,127.0.0.1:6381The proxy probes each node with INFO replication, forwards incoming connections to the current leader, and redirects new connections automatically when leadership changes—no client reconfiguration required.
By default logs go to stdout in text format. When --logdir is set, a JSON redis.log is also created. Sampling (enabled by default) throttles repeated messages; adjust via --log-sampling-* flags or disable entirely with --log-sampling=false.
go test ./...
golangci-lint runSome packages require write access to $GOCACHE; set GOCACHE if your environment is read-only.
tiny-redis implements a large subset of Redis commands. Highlights per data type:
- String:
GET,SET,MSET,MGET,INCR,DECR,SETEX,SETNX,APPEND, etc. - Hash:
HSET,HGET,HDEL,HMGET,HINCRBY,HRANDFIELD,HSTRLEN. - List:
LPUSH,RPUSH,LPOP,RPOP,LRANGE,LSET,LTRIM. - Set:
SADD,SREM,SMEMBERS,SINTER,SUNION,SDIFF,SRANDMEMBER. - Sorted Set:
ZADD,ZREM,ZINCRBY,ZPOPMAX,ZCOUNT. - Key / Admin:
DEL,EXPIRE,TTL,TYPE,PING,INFO.
See the actual command registration under pkg/memdb for the authoritative list.
.
├── cmd/ # CLI entrypoint
├── pkg/
│ ├── RESP/ # RESP parser/encoder
│ ├── cluster/ # Raft node orchestration
│ ├── logger/ # slog-based logging helpers
│ ├── memdb/ # in-memory database & commands
│ └── server/ # TCP server & connection handler
├── data/ # default Raft data directory (created at runtime)
├── Dockerfile
├── go.mod / go.sum
└── README.md
- More complete Redis command coverage (transactions, pub/sub).
- Replication read forwarding for followers.
- Metrics endpoints and admin tools.
- Optional RDB/AOF export.
Contributions & issues are welcome! Docker build with greentea GC (default in Dockerfile):
docker build -t tinyredis:latest .
# or override at build time
docker build --build-arg GOEXPERIMENT=greenteagc -t tinyredis:latest .