How to stunnel to Redis on-demand with stunredis


Introducing stunredis, a script to turn the trickiness of configuring a TLS/SSL tunnel for Redis into an automated breeze, and showing you how the magic is done. If you have TLS/SSL secured Redis, you'll want this.

When we introduced TLS/SSL connections to Redis at Compose we knew we would have some explaining and teaching to do. The thing with Redis is there's no out of the box TLS/SSL support but there is a community formula - using a TLS/SSL enabled proxy in front of the database server - to provide that functionality. That formula has led to many, if not most Redis drivers and libraries knowing about TLS or supporting the informal "rediss://" protocol. It all works really.

There's just one problem: the redis-cli command. It has no idea how to talk TLS/SSL. The initial solution to that problem is, as we documented at the time, to install and configure the stunnel utility which wraps a connection in a TLS/SSL encryption tunnel and then send the redis-cli connection down that. And that works.

The only thing is if you have more than one Redis deployment to work with. Then, for each one, you have to go edit /usr/local/etc/stunnel/stunnel.conf file and add an entry for that server and allocate a port for that server... and you may only need to just dive in for a minute to run a couple of commands. There had to be an easier way.

The easier way

We've put together a script called which uses the stunnel utility but in such a way that there's no configuration file. You'll find the script and associated files to download in the ibm-watson-data-lab/stunredis Github repository. Download it and chmod u+x to make the script executable.

Once you have done that, run ./ All you need to pass to the script is the connection string for a deployment and the script does the rest. Here's an example session with it:

❯ ./ rediss://
2018.03.15 10:46:24 LOG5[ui]: stunnel 5.44 on x86_64-apple-darwin17.2.0 platform  
2018.03.15 10:46:24 LOG5[ui]: Compiled with OpenSSL 1.0.2m  2 Nov 2017  
2018.03.15 10:46:24 LOG5[ui]: Running  with OpenSSL 1.0.2n  7 Dec 2017  
2018.03.15 10:46:24 LOG5[ui]: Threading:PTHREAD Sockets:POLL,IPv6 TLS:ENGINE,FIPS,OCSP,PSK,SNI  
2018.03.15 10:46:24 LOG5[ui]: Reading configuration from descriptor 0  
2018.03.15 10:46:24 LOG5[ui]: UTF-8 byte order mark not detected  
2018.03.15 10:46:24 LOG5[ui]: FIPS mode disabled  
2018.03.15 10:46:25 LOG5[ui]: Configuration successful  
2018.03.15 10:46:26 LOG5[0]: Service [redis-cli] accepted connection from  
2018.03.15 10:46:26 LOG5[0]: s_connect: connected  
2018.03.15 10:46:26 LOG5[0]: Service [redis-cli] connected remote server from  
2018.03.15 10:46:26 LOG5[0]: Certificate accepted at depth=0:> keys *  
1) "words">  

Here you can see stunnel starting up and making the connection and letting the user interact through redis-cli. When the user exits the redis-cli session...

2018.03.15 10:46:34 LOG5[0]: Connection closed: 75 byte(s) sent to TLS, 10183 byte(s) sent to socket  
2018.03.15 10:46:34 LOG5[ui]: Terminated  

... the script closes down the stunnel session.

Before doing this, of course, you'll need to install the stunnel and redis-cli commands. On macOS, we recommend the Homebrew package manager; once installed just run brew install redis stunnel and you'll be good to run the script.

How it works

This all starts with a discovery. We can only assume that someone else somewhere had a similar need to create on the fly configurations for stunnel because one of the options on the command is -fd N which tells it to take its configuration data from a numbered file descriptor. We'll use 0 to take that input from stdin.

What we put into stdin, by way of an echo -e command is a string we've composed to look like an appropriate configuration file. Here's the code that builds that string:

# Now we create our configuration file as a variable

For our example session above, the result would look like this:


The hostname and port - and combined host and port - have been parsed from the connection string. The same code also extracts the password from the connection string, but we don't use that in the stunnel configuration.

That "6830" in the accept= line is the setting for the local port. The value is actually our default setting for a port. You can override it with your own local port by adding the port number as a separate parameter after the connection string when running the script. You will want to do that if you want to run two or more instances of this script at the same time.

About lechain.pem

Finally, there's the CAfile= setting. This points to a file with the certificates needed to verify the connection. We use a file called lechain.pem which, for simplicity, is located in the same directory as the script. You can move it where ever you want, as long as you change the variable, set earlier in the script, which contains the path to the certificates.

The lechain.pem file is a sample of the verification chain for Lets Encrypt. It does work, but do not use for production if you are concerned about correctness; as with all things security related, get your own version from a trusted source or sources.

You can create your own version of lechain.pem by downloading and combining the contents of the Let's Encrypt X3 Cross-signed PEM file and the IdenTrust Root for X3. The latter link's content will need to be wrapped in the same -----BEGIN CERTIFICATE-----/-----END CERTIFICATE----- lines that the first link's content is wrapped in. Consult lechain.pem for an example of how it should look.

Back to the script

So now we have a variable full of configuration, a password we haven't used yet and a setting for a local port. It's time to bring them all together. First, we run stunnel. The configuration will stop it running as a daemon, but we'll run it in the background anyway otherwise we'd stop here.

echo -e $stunnelconf | stunnel -fd 0 &  

Now, we grab the process id of that stunnel process for later and, because it may still be setting up, we sleep for a second before carrying on.

sleep 1  

We can now run the redis-cli command. We pass it the local port setting we embedded in the configuration and the password as derived from the connection string.

redis-cli -p $LOCALPORT -a ${pass}  

The user will now be in redis-cli and able to issue commands. When the user is done and they exit redis-cli, we just need to tidy up by killing off the stunnel process.

kill $stunnelpid  

Wrapping up

You now have a way to stunnel to Redis on demand with This is the first released version and we're sure people will have their own enhancements or fixes for it. That's why we're more than happy to accept pull requests on it. This version is tested on macOS and Linux; let us know if you need a Windows version by adding to the repository's issues.


Read more articles about Compose databases - use our Curated Collections Guide for articles on each database type. If you have any feedback about this or any other Compose article, drop the Compose Articles team a line at We're happy to hear from you.

attribution Marc Sendra martorell

Dj Walker-Morgan
Dj Walker-Morgan was Compose's resident Content Curator, and has been both a developer and writer since Apples came in II flavors and Commodores had Pets. Love this article? Head over to Dj Walker-Morgan’s author page to keep reading.

Conquer the Data Layer

Spend your time developing apps, not managing databases.