Redix: a pure Elixir Redis client built around pipelines and reconnection
Fast, pipelined, resilient Redis driver for Elixir. ๐
At a glance
- What is it?
- Redix is a Redis and Valkey client for Elixir that sends commands as lists of strings instead of wrapping each one in an Elixir function, and it ships pipelining, automatic reconnection, Pub/Sub, SSL and Redis Sentinel support. The trade-off is that it stays deliberately low level, so most applications still need to think about pooling.
- Who is it for?
- Adopt Redix when you want a Redis client for Elixir that stays close to the wire, gives you pipelines and automatic reconnection without a wrapper function per command, and speaks to Redis Sentinel without extra machinery.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly Elixir, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A client that sends command lists instead of wrapping commands
Redix is written in pure Elixir and, in the README's own words, is a Redis and Valkey client with focus on speed, correctness and resiliency. The design decision that shapes everything else is stated early: Redix is simple, it does not wrap Redis commands with Elixir functions, it only provides functions to send any Redis command to the server. A command is expressed as a list of strings making up the command and its arguments.
That means there is no `Redix.get/2` and no `Redix.set/3`. There is one function that sends a command and one that sends a pipeline, and the shape of the call is the shape of the wire protocol.
Redix.command(conn, ["SET", "mykey", "foo"])
#=> {:ok, "OK"}
Redix.command(conn, ["GET", "mykey"])
#=> {:ok, "foo"}The feature list in the README names six things: an idiomatic interface for sending commands, pipelining, resiliency through automatic reconnections, Pub/Sub, SSL, and Redis Sentinel. The first item is the only one that needs a caveat, since idiomatic here means idiomatic Elixir for sending a command, not idiomatic Elixir for each Redis operation.
For a reader deciding whether to adopt this, the honest framing is that Redix occupies the same layer as the Redis protocol itself. That buys you control over batching and over connection behaviour. It also means you are the one who has to know which Redis command you want and what shape its reply takes.
Pipelines are lists of command lists sent in one go
The second core function is the pipeline, and it is where the throughput argument comes from. A pipeline is a list of commands sent all at once to Redis, and Redis replies with a list of responses.
Redix.pipeline(conn, [["INCR", "foo"], ["INCR", "foo"], ["INCRBY", "foo", "2"]])
#=> {:ok, [1, 2, 4]}The example is a good illustration of why this matters for correctness as well as speed. Three separate `INCR` calls on the same key would each cost a round trip, and would interleave with any other client touching that key between them. In a pipeline they are sent in one round trip and Redis replies in order, which is what makes the result `[1, 2, 4]`.
Both functions return the same shape, `{:ok, result}` or `{:error, reason}`, and the README also provides bang variants for the case where you would rather raise on an error than pattern match on it.
Redix.command!(conn, ["PING"])
#=> "PONG"
Redix.pipeline!(conn, [["SET", "mykey", "foo"], ["GET", "mykey"]])
#=> ["OK", "foo"]One detail is easy to miss and matters for pipelines in particular. The reply list is positional, one entry per command sent. If you build a pipeline dynamically, the reply array and the command list must stay in step, and a mismatch shows up as a shifted result rather than an error.
Adding the dependency and starting a connection
Redix is a Hex package, so it goes into the dependency list of your `mix.exs`.
defp deps do
[
{:redix, "~> 1.1"},
{:castore, ">= 0.0.0"}
]
endThe `:castore` entry is conditional. The README says that if you plan on connecting to a Redis server over SSL you may want to add the optional `:castore` dependency as well, and it is the CA certificate store used to verify the server certificate.
After that you fetch the dependencies.
mix deps.getConnections are started with `start_link/0,1,2`, and there are two shapes of the argument. A keyword list configures the connection directly, and a URL string configures it in the familiar `redis://host:port/db` form, with an optional name for the process.
{:ok, conn} = Redix.start_link(host: "example.com", port: 5000)
{:ok, conn} = Redix.start_link("redis://localhost:6379/3", name: :redix)The second example is the one to notice, because the database index travels inside the URL as a path segment. The database `3` is selected at connection time, which means a single Redix process talks to one logical database on one server, and the name `:redix` registers the process under that name so it can be looked up later.
The pattern matters more than the arguments. Redix is an OTP process started under a supervisor, and it returns `{:ok, conn}` where `conn` is the pid you then pass to every command and pipeline call.
Reconnection is automatic, and the backoff is configurable
Resiliency in Redix means the connection survives a network error without the application having to notice. The README's description is plain: if the connection to Redis drops, Redix will automatically try to reconnect periodically at a given backoff interval.
This is the feature that decides whether you need a supervisor restart strategy for your Redis client at all. A supervisor that restarts a dead connection process is workable but disruptive, since in-flight commands are lost and the process identity changes. A client that reconnects on its own keeps the same pid, and the application code above it does not have to change.
The README deliberately does not spell out the timings, and it points twice: at the `Redix` module documentation and at the Reconnections page in the documentation. The options and the exact reconnection behaviour live there, so if the backoff curve matters to you, that page, not the README, is where to read it.
The same pattern appears for Sentinel, where the reconnection path has extra steps, which is worth understanding before you rely on it. The README describes it in one sentence that is easy to miss: every time the connection needs to connect to a Redis server, the first time or after a disconnection, it will try to connect to one of the sentinels in order to ask that sentinel for the current primary or a replica.
Sentinel discovery uses a sentinel list and a group name
Redis Sentinel support is built in, with no separate module to configure. You pass a list of sentinel URLs when starting a connection.
sentinels = ["redis://sent1.example.com:26379", "redis://sent2.example.com:26379"]
{:ok, primary} = Redix.start_link(sentinel: [sentinels: sentinels, group: "main"])The two parameters inside the `:sentinel` option do different jobs. The sentinels list is the set of addresses Redix asks, and the group is the name of the monitored group it wants the primary or a replica for. The return value is named `primary` in the README example, which reflects the role Redix asked the sentinel for.
The terminology is a deliberate choice and the README spends a paragraph on it. Redix does not use the terms master and slave that are usually used with Redis Sentinel, and the reason given is a stated disagreement with those terms. The library uses primary and replica instead, and the README links a discussion in the Redis repository on the same subject for readers who want the longer argument.
If you are mixing Redix with other tooling in the same stack, that vocabulary difference is a real interoperability point. Configuration keys, sentinel group names and log messages will read differently from what you see elsewhere in a Redis deployment, so a team that already speaks in master and slave terms will need a word of explanation the first time they read a Redix log line.
Pub/Sub is a separate process that delivers Elixir messages
Pub/Sub is not a mode of the main connection, it is a separate process with its own API. You start it with `Redix.PubSub.start_link/2`, and most of the interaction happens through Elixir messages rather than through return values, which the README says simulate a Pub/Sub interaction with the publish and subscribe server.
{:ok, pubsub} = Redix.PubSub.start_link()
Redix.PubSub.subscribe(pubsub, "my_channel", self())
#=> {:ok, ref}The subscribe call takes the channel and the pid that should receive the messages, and it returns a reference. The confirmation of the subscription arrives later as an Elixir message, which means a caller has to wait for it rather than read a result from the call.
receive do
{:redix_pubsub, ^pubsub, ^ref, :subscribed, %{channel: "my_channel"}} -> :ok
endPublished messages arrive in the same envelope shape, with the payload in a map under the `:message` tag.
receive do
{:redix_pubsub, ^pubsub, ^ref, :message, %{channel: "my_channel", payload: "hello"}} ->
IO.puts("Received a message!")
endThe pinned variables `^pubsub` and `^ref` in the pattern are the important part for anyone writing a supervisor or a GenServer around this. They let one process receive from several pub/sub connections and several channels at once and still tell which message belongs to which subscription, because the message carries both the process reference and the subscription reference.
Pooling is your problem, and the docs say so
The README has a section headed Using Redix in the Real World, and its first claim is that Redix is low-level but still built to handle most things thrown at it. The next sentence is the one to plan around: for many applications you can avoid pooling with little or no impact on performance, and the Real world usage page covers this along with pooling strategies that work better with Redix.
That is the honest trade-off. A client that speaks the protocol directly tends to be fast and predictable, but it also leaves connection management to the application, and a Redis connection is not free on the server. The number of Redix processes you start becomes the number of connections your application holds open.
The repository layout shows the project is serious about the operational side of this. There is a `benchmarks/` directory at the top level and a `pages/` directory that holds the documentation pages the README links to, including the Real world usage page and the Reconnections page. A `CHANGELOG.md` is present, which is worth noting because the README itself points out that it describes the main branch rather than the latest released version on Hex, so the changelog is where you check what a given release actually changed.
There is also a `docker-compose.yml` in the repository root, and the README explains why. Running the test suite requires several Redis instances, for Pub/Sub and for Sentinel, and the compose file spins them all up with a single command.
docker compose upAfter that the tests run with `mix test`. The compose file itself is more interesting than a developer convenience: it defines services for a base instance, a Pub/Sub instance, an instance with password authentication, one with ACLs, sentinel with and without authentication, and a stunnel-fronted service for TLS, with host-side ports parameterised by `REDIX_*_PORT` environment variables. That is a good picture of the configurations the library is tested against.
Editorial conclusion
Adopt Redix when you want a Redis client for Elixir that stays close to the wire, gives you pipelines and automatic reconnection without a wrapper function per command, and speaks to Redis Sentinel without extra machinery. Do not adopt it if you want an idiomatic function per Redis command, because the README says plainly that Redix does not wrap Redis commands with Elixir functions, and expect to read the Reconnections and Real world usage pages before you tune connection counts. Before you build on it, check that your `mix.exs` pins `{:redix, "~> 1.1"}` and that you add `{:castore, ">= 0.0.0"}` if you connect over SSL, and remember that the README describes the main branch, so the documentation for the Hex version you actually install may differ.
Frequently asked questions
How do I add Redix to an Elixir project and connect to Redis?
Add `{:redix, "~> 1.1"}` to the deps in `mix.exs`, run `mix deps.get`, then start a connection with `Redix.start_link/0,1,2`. The README shows both a keyword list form such as `Redix.start_link(host: "example.com", port: 5000)` and a URL form such as `Redix.start_link("redis://localhost:6379/3", name: :redix)`.
Does Redix provide a function for each Redis command?
No. The README states that Redix does not wrap Redis commands with Elixir functions, and only provides functions to send any Redis command to the server. A command is expressed as a list of strings, for example `Redix.command(conn, ["SET", "mykey", "foo"])`.
How do I send several Redis commands at once with Redix?
Use `Redix.pipeline/2,3` with a list of command lists, for example `Redix.pipeline(conn, [["INCR", "foo"], ["INCR", "foo"], ["INCRBY", "foo", "2"]])`, which returns `{:ok, [1, 2, 4]}`. The bang variant `Redix.pipeline!/2,3` returns the list of replies directly and raises on error.
What happens when the Redis connection drops?
Redix is resilient against network errors and will automatically try to reconnect periodically at a given backoff interval. The README points to the Redix module documentation and the Reconnections page for the available options and the exact reconnection behaviour.
How do I use Redis Sentinel and Pub/Sub with Redix?
For Sentinel, pass a list of sentinel URLs and a group name when starting the connection, as in `Redix.start_link(sentinel: [sentinels: sentinels, group: "main"])`. For Pub/Sub, start a separate `Redix.PubSub` process, call `Redix.PubSub.subscribe(pubsub, "my_channel", self())`, and receive the subscription confirmation and published messages as Elixir messages.
How do I run the Redix test suite?
The README says you need Redis running locally and that the setup is somewhat complex, because several instances are required for Pub/Sub and Sentinel. The repository includes a `docker-compose.yml` so you can run `docker compose up` and then use `mix test`.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/whatyouhide-redix)Was this useful?