miniredis is a free, open source databases project written in Go and released under MIT. It has 3,620 GitHub stars, 257 forks and 6 open issues, and was last pushed yesterday. On this registry it ranks #169 of 203 tracked projects in Databases, with 5 head-to-head comparisons available.

What is miniredis?

miniredis is a pure Go in-memory implementation of parts of the Redis server that speaks a real TCP interface inside a Go unittest process, built for Go developers who need to exercise Redis-backed code without running an external Redis instance.

What it is

Miniredis is a Redis replacement written in Go and consumed as the module github.com/alicebob/miniredis/v2. It implements a substantial subset of the Redis server, covering the connection, key, string, hash, list, set, sorted set, stream, pub/sub, scripting, GEO, cluster and HyperLogLog command groups. Because it exposes a real TCP interface rather than an in-process stub, an existing Redis client library can connect to it unchanged. It is licensed under MIT and sits in the Infrastructure & Operations / Databases category of this registry.

The concrete problem it solves is testing code that depends on Redis without turning every test into a full-blown integration test. The README states the project saves you from using mock code, and since the Redis server lives in the test process you can query for values directly, without going through the server stack. There are no dependencies on external binaries, which makes it straightforward to integrate into automated build processes. It is the Redis counterpart of net/http/httptest.

Key capabilities

  • Implements Redis command families for strings, hashes, lists (including the blocking forms BLPOP, BRPOP, BLMOVE and BRPOPLPUSH), sets, sorted sets, streams (XADD, XREAD, XREADGROUP, XGROUP CREATE), HyperLogLog (PFADD, PFCOUNT, PFMERGE) and GEO (GEOADD, GEODIST, GEOSEARCH).
  • Provides a real TCP interface, so the test target connects through an ordinary Redis client rather than a shim.
  • Supports transactions through MULTI, EXEC, DISCARD, WATCH and UNWATCH.
  • Implements scripting with EVAL, EVALSHA, SCRIPT LOAD, SCRIPT EXISTS and SCRIPT FLUSH.
  • Implements cluster introspection commands: CLUSTER SLOTS, CLUSTER KEYSLOT, CLUSTER NODES and CLUSTER SHARDS.
  • Gives deterministic control over expiry: TTL returns a time.Duration, m.FastForward(d) decrements all TTLs and removes keys at or below zero, m.SetTime(t) sets the base for (P)EXPIREAT conversion and the value returned by TIME, and m.ClockTTL(true) makes TTLs decrease as the clock advances.
  • Enables authentication checks through RequireAuth() for AUTH and RequireUserAuth() for HELLO, and supports deterministic randomness via m.Seed(...) for RANDOMKEY, SPOP and SRANDMEMBER.

Who uses it and how

  • Go teams writing unit tests for services that depend on Redis, who want test-process isolation instead of a live integration environment.
  • Continuous integration pipelines that cannot install or run an external redis-server, since miniredis has no dependencies on external binaries.
  • Test suites that need expiry behaviour without sleeping: a call to m.FastForward(d) advances all TTLs at once, and expired keys are removed lazily whenever a database is accessed.
  • Tests that exercise pub/sub, scripting through EVAL and EVALSHA, or blocking list operations, all of which run inside the same process as the test.
  • Tests whose results depend on random selection, made reproducible through m.Seed(...).

Getting started

Miniredis is installed as a Go module and imported as github.com/alicebob/miniredis/v2, which the README explicitly requires: be sure to import v2. No Docker image, compose file or hosted option is mentioned in the facts.

How it compares

Among the tools named in the facts, the README places miniredis as the Redis version of net/http/httptest, which makes it the in-process counterpart to running an actual Redis server for a test. No list of paid products it replaces is provided, so no licence, hosting or cost comparison can be drawn here.

When to use it — and when not to

A self-hoster operating miniredis runs nothing extra: it is a library inside the test binary, with no database, object storage or SMTP service to maintain. It should not be chosen by teams that need full Redis fidelity for integration or performance testing, or by non-Go projects, since it is scoped to Go unittests. The honest limitations in the facts are that only parts of the Redis server are implemented, several commands are partial (DUMP and RESTORE only handle string keys, INFO returns only the clients section with connected_clients, and COMMAND is partial), GEOHASH is struck through in the command list, and TTLs do not decrease automatically unless ClockTTL(true) is used.

project readme (upstream, from github) — read inline

Miniredis

Pure Go Redis test server, used in Go unittests.

Sometimes you want to test code which uses Redis, without making it a full-blown integration test. Miniredis implements (parts of) the Redis server, to be used in unittests. It enables a simple, cheap, in-memory, Redis replacement, with a real TCP interface. Think of it as the Redis version of net/http/httptest.

It saves you from using mock code, and since the redis server lives in the test process you can query for values directly, without going through the server stack.

There are no dependencies on external binaries, so you can easily integrate it in automated build processes.

Be sure to import v2:

import "github.com/alicebob/miniredis/v2"

Commands

Implemented commands:

  • Connection
    • AUTH -- see RequireAuth()
    • ECHO
    • HELLO -- see RequireUserAuth()
    • PING
    • SELECT
    • SWAPDB
    • QUIT
  • Key
    • COPY
    • DEL
    • DUMP -- partly, only handles string keys
    • EXISTS
    • EXPIRE
    • EXPIREAT
    • EXPIRETIME
    • KEYS
    • MOVE
    • PERSIST
    • PEXPIRE
    • PEXPIREAT
    • PEXPIRETIME
    • PTTL
    • RANDOMKEY -- see m.Seed(...)
    • RENAME
    • RENAMENX
    • RESTORE -- partly, only handles string keys
    • SCAN
    • TOUCH
    • TTL
    • TYPE
    • UNLINK
    • WAIT -- no-op
  • Transactions
    • DISCARD
    • EXEC
    • MULTI
    • UNWATCH
    • WATCH
  • Server
    • DBSIZE
    • FLUSHALL
    • FLUSHDB
    • TIME -- returns time.Now() or value set by SetTime()
    • COMMAND -- partly
    • INFO -- partly, returns only "clients" section with one field "connected_clients"
  • String keys
    • APPEND
    • BITCOUNT
    • BITOP
    • BITPOS
    • DECR
    • DECRBY
    • DELEX -- partly
    • GET
    • GETBIT
    • GETDEL
    • GETEX
    • GETRANGE
    • GETSET
    • INCR
    • INCRBY
    • INCRBYFLOAT
    • MGET
    • MSET
    • MSETNX
    • PSETEX
    • SET
    • SETBIT
    • SETEX
    • SETNX
    • SETRANGE
    • STRLEN
  • Hash keys
    • HDEL
    • HEXISTS
    • HEXPIRE
    • HGET
    • HGETALL
    • HINCRBY
    • HINCRBYFLOAT
    • HKEYS
    • HLEN
    • HMGET
    • HMSET
    • HPERSIST
    • HPTTL
    • HRANDFIELD
    • HSET
    • HSETEX
    • HSETNX
    • HSTRLEN
    • HTTL
    • HVALS
    • HSCAN
  • List keys
    • BLPOP
    • BRPOP
    • BRPOPLPUSH
    • LINDEX
    • LINSERT
    • LLEN
    • LPOP
    • LPUSH
    • LPUSHX
    • LRANGE
    • LREM
    • LSET
    • LTRIM
    • RPOP
    • RPOPLPUSH
    • RPUSH
    • RPUSHX
    • LMOVE
    • BLMOVE
  • Pub/Sub
    • PSUBSCRIBE
    • PUBLISH
    • PUBSUB
    • PUNSUBSCRIBE
    • SUBSCRIBE
    • UNSUBSCRIBE
  • Set keys
    • SADD
    • SCARD
    • SDIFF
    • SDIFFSTORE
    • SINTER
    • SINTERSTORE
    • SINTERCARD
    • SISMEMBER
    • SMEMBERS
    • SMISMEMBER
    • SMOVE
    • SPOP -- see m.Seed(...)
    • SRANDMEMBER -- see m.Seed(...)
    • SREM
    • SSCAN
    • SUNION
    • SUNIONSTORE
  • Sorted Set keys
    • BZPOPMAX
    • BZPOPMIN
    • ZADD
    • ZCARD
    • ZCOUNT
    • ZINCRBY
    • ZINTER
    • ZINTERSTORE
    • ZLEXCOUNT
    • ZPOPMIN
    • ZPOPMAX
    • ZRANDMEMBER
    • ZRANGE
    • ZRANGEBYLEX
    • ZRANGEBYSCORE
    • ZRANK
    • ZREM
    • ZREMRANGEBYLEX
    • ZREMRANGEBYRANK
    • ZREMRANGEBYSCORE
    • ZREVRANGE
    • ZREVRANGEBYLEX
    • ZREVRANGEBYSCORE
    • ZREVRANK
    • ZSCORE
    • ZUNION
    • ZUNIONSTORE
    • ZSCAN
  • Stream keys
    • XACK
    • XADD
    • XAUTOCLAIM
    • XCLAIM
    • XDEL
    • XGROUP CREATE
    • XGROUP CREATECONSUMER
    • XGROUP DESTROY
    • XGROUP DELCONSUMER
    • XINFO STREAM -- partly
    • XINFO GROUPS
    • XINFO CONSUMERS -- partly
    • XLEN
    • XRANGE
    • XREAD
    • XREADGROUP
    • XREVRANGE
    • XPENDING
    • XTRIM
  • Scripting
    • EVAL
    • EVALSHA
    • SCRIPT LOAD
    • SCRIPT EXISTS
    • SCRIPT FLUSH
  • GEO
    • GEOADD
    • GEODIST
    • GEOHASH
    • GEOPOS
    • GEORADIUS
    • GEORADIUS_RO
    • GEORADIUSBYMEMBER
    • GEORADIUSBYMEMBER_RO
    • GEOSEARCH
  • Cluster
    • CLUSTER SLOTS
    • CLUSTER KEYSLOT
    • CLUSTER NODES
    • CLUSTER SHARDS
  • HyperLogLog
    • PFADD
    • PFCOUNT
    • PFMERGE

TTLs, key expiration, and time

Since miniredis is intended to be used in unittests TTLs don't decrease automatically. You can use TTL() to get the TTL (as a time.Duration) of a key. It will return 0 when no TTL is set.

m.FastForward(d) can be used to decrement all TTLs. All TTLs which become <= 0 will be removed.

EXPIREAT and PEXPIREAT values will be converted to a duration. For that you can either set m.SetTime(t) to use that time as the base for the (P)EXPIREAT conversion, or don't call SetTime(), in which case time.Now() will be used.

SetTime() also sets the value returned by TIME, which defaults to time.Now(). It is not updated by FastForward, only by SetTime.

m.ClockTTL(true) makes TTLs decrease as the clock (time.Now(), or the SetTime() value) advances, instead of only via FastForward. Expired keys are removed lazily, whenever a database is accessed via a command or a Miniredis-level accessor. Combined with SetTime() the expiry stays coherent with TIME and (P)EXPIREAT. Note that against the wall clock this makes expiry depend on test execution speed, which is why it's not the default.

testing/synctest

miniredis works inside a testing/synctest bubble (Go 1.25+). Since real TCP connections can't be used there, connect with m.Dial(), which serves the connection on an in-memory pipe — most redis clients accept it as a custom dialer. NewMiniRedis() is the non-started constructor: with Dial() no listener is ever created. Inside a bubble time.Now() is the bubble's fake clock, so with ClockTTL(true) a plain time.Sleep() expires keys — no FastForward needed:

synctest.Test(t, func(t *testing.T) {
	m := miniredis.NewMiniRedis()
	defer m.Close()
	m.ClockTTL(true)

	client := redis.NewClient(&redis.Options{
		Dialer: func(ctx context.Context, _, _ string) (net.Conn, error) {
			return m.Dial()
		},
	})

	ctx := context.Background()
	client.Set(ctx, "session", "v", time.Hour)
	time.Sleep(time.Hour + time.Second) // instant, and deterministic
	// key is expired now
})

Randomness and Seed()

Miniredis will use math/rand's global RNG for randomness unless a seed is provided by calling m.Seed(...). If a seed is provided, then miniredis will use its own RNG based on that seed.

Commands which use randomness are: RANDOMKEY, SPOP, and SRANDMEMBER.

Example


import (
    ...
    "github.com/alicebob/miniredis/v2"
    ...
)

func TestSomething(t *testing.T) {
	s := miniredis.RunT(t)

	// Optionally set some keys your code expects:
	s.Set("foo", "bar")
	s.HSet("some", "other", "key")

	// Run your code and see if it behaves.
	// An example using the redigo library from "github.com/gomodule/redigo/redis":
	c, err := redis.Dial("tcp", s.Addr())
	_, err = c.Do("SET", "foo", "bar")

	// Optionally check values in redis...
	if got, err := s.Get("foo"); err != nil || got != "bar" {
		t.Error("'foo' has the wrong value")
	}
	// ... or use a helper for that:
	s.CheckGet(t, "foo", "bar")

	// TTL and expiration:
	s.Set("foo", "bar")
	s.SetTTL("foo", 10*time.Second)
	s.FastForward(11 * time.Second)
	if s.Exists("foo") {
		t.Fatal("'foo' should not have existed anymore")
	}
}

Not supported

Commands which will probably not be implemented:

  • CLUSTER (all)
    • CLUSTER *
    • READONLY
    • READWRITE
  • Key
    • MIGRATE
    • OBJECT
  • Scripting
    • FCALL / FCALL_RO *
    • FUNCTION *
    • SCRIPT DEBUG
    • SCRIPT KILL
  • Server
    • BGSAVE
    • BGWRITEAOF
    • CLIENT *
    • CONFIG *
    • DEBUG *
    • LASTSAVE
    • MONITOR
    • ROLE
    • SAVE
    • SHUTDOWN
    • SLAVEOF
    • SLOWLOG
    • SYNC

&c.

Integration tests are run against Redis 8.4.0. The ./integration subdir compares miniredis against a real redis instance.

The Redis 6 RESP3 protocol is supported. If there are problems, please open an issue.

If you want to test Redis Sentinel have a look at minisentinel.

A changelog is kept at CHANGELOG.md.

Go Reference

Frequently asked questions

Is miniredis free to use?

miniredis is open source under the MIT licence. There is no licence fee and no seat count — you can self-host it or, where the project offers one, pay a vendor for a managed version instead.

What does miniredis do?

Pure Go Redis server for Go unittests

What is miniredis written in?

miniredis is primarily written in Go. Its source is publicly available at https://github.com/alicebob/miniredis, and it has 3,620 GitHub stars.