A guide to using built-in TLS in Redis

Redis version 6 added TLS as a built-in feature. This makes me super happy, because I’m now able to use Redis as a store with my applications running on App Engine considering that the traffic is encrypted, without additional tools or a paid Redis Cloud plan.

Keep in mind that you will have to build Redis with TLS support at compile time. See https://redis.io/topics/encryption.

I couldn’t find an end-to-end example for setting up TLS (not very surprising considering that the feature was released April 30 this year). So here we go.

Generate cert files #

To use TLS with Redis, you’ll have to generate:

  1. a certificate-key pair for the server (redis.crt, redis.key), and
  2. a root CA certificate (ca.crt)

The TL;DR is that you run

$ FQDN=<redis-server-ip> make generate

with this Makefile:

OUTDIR ?= tls
# may be /etc/pki/tls in some machines.
# use `openssl version -a | grep OPENSSLDIR` to find out.
OPENSSLDIR ?= /etc/ssl

.PHONY: generate
generate: prepare redis.crt clean

.PHONY: prepare
    mkdir ${OUTDIR}

.PHONY: clean
    rm -f ${OUTDIR}/openssl.cnf

    cat ${OPENSSLDIR}/openssl.cnf > ${OUTDIR}/openssl.cnf
    echo "" >> ${OUTDIR}/openssl.cnf
    echo "[ san_env ]" >> ${OUTDIR}/openssl.cnf
    echo "subjectAltName = IP:${FQDN}" >> ${OUTDIR}/openssl.cnf

    openssl genrsa 4096 > ${OUTDIR}/ca.key

ca.crt: ca.key
    openssl req \
        -new \
        -x509 \
        -nodes \
        -sha256 \
        -key ${OUTDIR}/ca.key \
        -days 3650 \
        -subj "/C=AU/CN=example" \
        -out ${OUTDIR}/ca.crt

redis.csr: openssl.cnf
    # is -extensions necessary?
    # https://security.stackexchange.com/a/86999
    SAN=IP:$(FQDN) openssl req \
        -reqexts san_env \
        -extensions san_env \
        -config ${OUTDIR}/openssl.cnf \
        -newkey rsa:4096 \
        -nodes -sha256 \
        -keyout ${OUTDIR}/redis.key \
        -subj "/C=AU/CN=$(FQDN)" \
        -out ${OUTDIR}/redis.csr

redis.crt: openssl.cnf ca.key ca.crt redis.csr
    SAN=IP:$(FQDN) openssl x509 \
        -req -sha256 \
        -extfile ${OUTDIR}/openssl.cnf \
        -extensions san_env \
        -days 3650 \
        -in ${OUTDIR}/redis.csr \
        -CA ${OUTDIR}/ca.crt \
        -CAkey ${OUTDIR}/ca.key \
        -CAcreateserial \
        -out ${OUTDIR}/redis.crt

This will produce the required cert files in a directory named tlsby default. Optionally you can can set OUTDIR=<path> to specify a custom output directory. A couple of notes:

The Makefile is adapted from Stack Overflow.

Start the server #

Once you have Redis built with TLS support, you’ll have to set TLS-specific options in your redis.conf. Here are the relevant options from mine.

port 0
tls-port 6379
tls-cert-file tls/redis.crt
tls-key-file tls/redis.key
tls-ca-cert-file tls/ca.crt

You can find these options with detailed comments in the “TLS/SSL” section of a new redis.conf.

Place the generated cert files in your server. For the config above, I place my cert files in a directory named tls, relative to the directory from where I would start the Redis server.

With that done, you can start the Redis server.

$ ls
redis.conf  tls
$ redis-server redis.conf

Connect with a client #

With the server running, you’ll want to try connecting with a client. I’ve been able to connect successfully with redis-cli, Node.js programs, and Go programs.

redis-cli #

Likely the easiest client to connect with to a Redis TLS server. For simplicity and to potentially save debugging time, you should try this first, locally from the same machine running the Redis server.

Run redis-cli as you mostly would, with extra options specifying the cert files. Run a sample PING command to make sure you’ve connected successfully.

$ redis-cli --tls --cert tls/redis.crt --key tls/redis.key --cacert tls/ca.crt
redis> ping

As a soundness check, try omitting one of the options. It should fail to connect.

Node.js client #

For Node, I was using https://github.com/NodeRedis/node-redis, but https://github.com/luin/ioredis should work equally well.

Side note: In hindsight, I would have like to have used ioredis (and it’s what I’ll do in the future) because I disagree with node-redis’s handling of null/undefined in SET calls.

Both packages support a tls property in their client config object. The type SecureContextOptions is from tls.createSecureContext().

tls: SecureContextOptions

So you can do (code below uses some TypeScript syntax):

import * as fs from "fs"
import redis from "redis"

const redisHost = process.env["REDIS_HOST"]! // e.g. "", "", "localhost", "redis.acmecorp.com"

const client = redis.createClient({
    host: redisHost,
    port: 6379,
    tls: {
        cert: fs.readFileSync("redis/tls/redis.crt"), 
        key: fs.readFileSync("redis/tls/redis.key"), 
        ca: fs.readFileSync("redis/tls/ca.crt"), 

Go client #

I used https://github.com/go-redis/redis. The configuration is similar to Node’s. The package’s *redis.Options type has a field:

// TLS Config to use. When set TLS will be negotiated.
TLSConfig *tls.Config

You can do:

import (


func main() {
    redisHost := os.Getenv("REDIS_HOST") // e.g. "", "", "localhost", "redis.acmecorp.com"

    cert, err := tls.LoadX509KeyPair("redis/tls/redis.crt", "redis/tls/redis.key")
    if err != nil {

    caCert, err := ioutil.ReadFile("redis/tls/ca.crt")
    if err != nil {
    pool := x509.NewCertPool()

    client := redis.NewClient(&redis.Options{
        Addr: net.JoinHostPort(redisHost, "6379"),
        TLSConfig: &tls.Config{
            ServerName:   redisHost,
            Certificates: []tls.Certificate{cert},
            RootCAs:      pool,

And you should hopefully have functioning clients at this stage.


Now read this

Accidental removal of side-effect imports

Go has the ability to import a package purely for the package’s side-effects (i.e. you don’t actually use a symbol from the package in your code, but just want side-effects introduced by the package). As an example, look at package... Continue →