Generate free SSL certificates with Docker and LetsEncrypt

It’s been more than a year in the waiting, after I found out that Mozilla Foundation, Akamai, Cisco, and a bunch of other big players put their support into LetsEncrypt, a free certificate authority. Back then (I’m speaking as if 2014 was still in the time of Yugoslavia or USSR), you would have to go trough several complicated steps for generating a SSL certificate for free (client certificates, issuing certificate signing requests, worrying about key size, key formats, validating your certificate via e-mail, …). I promise, the wait is over.

Today you can use letsencrypt to automatically generate the certificate for you. They are valid for 90 days, so technically, all you need to do is regenerate them within this period. Since the whole process can be automated, it’s much easier than remembering a year from now to renew your SSL certificate and how to actually do it.

As I’m in a “let’s use docker for everything” mood in the recent time. On the encouragement of @OmgImAlexis I was reminded of letsencrypt and finally decided to give it a run. It was just a matter of finding a correct docker image, which wouldn’t give me a complete server, but can somehow use my existing server to communicate with their service.

In the end, the whole thing was so surprisingly easy that I’m wasting more time blogging about it, than I actually did by generating the SSL certificate. I’m just going to share with you my approach and break it down so you understand it.

ROOTPATH=$(dirname `pwd`);
docker run -it --rm \
    -v $ROOTPATH/conf:/etc/letsencrypt \
    -v /var/www/black-dev:/var/www xataz/letsencrypt \
    certonly --webroot --agree-tos -m \
    -w /var/www/ -d -d


KEY=$(readlink -f $SOURCE/privkey.pem)
CHAIN=$(readlink -f $SOURCE/fullchain.pem)

cp -f $KEY $DESTINATION/nginx/cert/server.key
cp -f $CHAIN $DESTINATION/nginx/cert/server.pem

if [ ! -f "$ROOTPATH/data/dhparams.pem" ]; then
    openssl dhparam -out $ROOTPATH/data/dhparams.pem 2048
mkdir $DESTINATION/nginx/cert -p
cp -f $ROOTPATH/data/dhparams.pem $DESTINATION/nginx/cert/dhparams.pem

So, this is literally the way I generate the needed files for my nginx setup. I organize my docker scripts under src, where I have several folders, respectively bin where I keep the above script, conf for where I keep the container config volume, data where I keep any … well, data. There are basically a few important things about this script.

Using the docker image

I have chosen the xataz/letsencrypt docker image because it was the first one which had a guide how to use it without the internal web server, or without another web server packaged in the docker image. I’m already running a nginx instance, which I want to use when generating the SSL certificate. You can visit the xataz/letsencrypt github page if you want some additional instructions for the docker image.

I keep my certificates under $DESTINATION in the folders nginx/cert and my web root in public_html. I’m mounting the webroot to the docker container, and using it with the -w option. I’m generating a SSL certificate for and, as defined with -d. The web root folder should be reachable from your web server, as the letsencrypt service verifies it by connecting to the domains you’re generating the SSL certificate for.

LetsEncrypt places the generated certificates in the archive folder, and creates symlinks under /etc/letsencrypt/live/. I’m resolving the symlinks with the readlink command, as I need to copy these generated files to my location under $DESTINATION/nginx/cert.


This is a file which improves your SSL security against the Logjam attack. Logjam is an attack against the Diffie-Helman key exchange which is used in the HTTPS protocol, as well as others. You can read more about it here: There are a few other attacks which have been publicized in the last years (Heartbleed, Pooddle,…), so it’s good to check your server against those.

I’m using Qualys SSL Labs page to check the installation of my SSL certificate, to see if I’m reasonably secure. The “key exchange” is one of the tests being done, and a custom dhparams file solves this issue.

SSL check for the current domain after generating a Let’s Encrypt SSL certificate.

nginx configuration

There is my configuration for nginx, which uses the key, the certificate chain, the dhparams, and configures some option to make the SSL termination as secure as possible at this time.

ssl_certificate /var/www/black-dev/;
ssl_certificate_key /var/www/black-dev/;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'AES128+EECDH:AES128+EDH';
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_dhparam /var/www/black-dev/;

Closing words

SSL Certificates are still far from perfect when it comes to the issues of “Trust” versus the convenience of obtaining a SSL certificate with as little effort as possible. LetsEncrypt is not perfect for everyone - this is why we still need to use other SSL certificate authorities to provide Extended Validation (EV) certificates identifiying persons or companies, and also Wildcard certificates (for now?). LetsEncrypt is a nice step forward as it makes SSL encryption viable for anyone just by beeing free and simple to use. I am looking forward towards a safer web, even if as a programmer I understand the appeal (or is it lazyness factor) of simple text protocols like HTTP.

Docker in this case has again proved to be a viable zero-touch alternative as opposed to installing and configuring software by hand.

A few days ago @drye asked me what I managed to run in docker on the Digital Ocean $10 VPS SSD instance, and I thought I’d just attach a list of it. These are the images I currently use, let me break them down as to what every one does:

  1. xataz/letsencrypt - for generating SSL certificates for various web sites on the server
  2. richarvey/nginx-php-fpm - for serving dynamic web pages (back-end)
  3. percona - a higher-performance fork of mysql with some added features
  4. nazarpc/phpmyadmin - a web server with phpmyadmin (i might get rid of this one because #2)
  5. node - the node.js runtime, with npm. I use it for developing and running node.js apps
  6. simplyintricate/hexo - hey, I run a blog. I also installed it with docker.
  7. renoirb/nginx-http2-luajit - this is my front-end/reverse proxy. I use lua for some development purposes (CGI, kinda)

While I have you here...

It would be great if you buy one of my books:

I promise you'll learn a lot more if you buy one. Buying a copy supports me writing more about similar topics. Say thank you and buy my books.

Feel free to send me an email if you want to book my time for consultancy/freelance services. I'm great at APIs, Go, Docker, VueJS and scaling services, among many other things.