How to set up SSL certificate for Nginx Docker container

Sun 28 July 2019

I have been using Docker for past few years as a dev environment. It's easy to set up, run and maintain. Recently I decided to containerise one of my projects and check if running docker on production would be as easy as running it locally.

One major difference was the way app is served. Locally I just ran dev server inside container but for production use I had to use real server with SSL certificate.

Before I was able to generate SSL certificate I had to setup my containerised app. I have added nginx container customised with my site config to the stack and configured volumes used to populate nginx container with SSL certificate.

Here's a simplified version of docker-compose.yml file I'm using (don't worry about SSL related volumes, we'll get back to them later):

version: "3.7"
    # app configuration
    # db configuration
      context: .
      dockerfile: docker/services/nginx/Dockerfile
    restart: always
      - "80:80"
      - "443:443"
      - ./docker/services/nginx/ssl/dh-param/dhparam.pem:/etc/ssl/certs/dhparam.pem
      - ./docker/services/nginx/ssl/etc/letsencrypt/live/
      - ./docker/services/nginx/ssl/etc/letsencrypt/live/

Next I had to set up SSL configuration in my site config.

server {
    listen 80;
    return 301 https://$host$request_uri;

server {
    listen 443 ssl;

    # other settings, like location blocks, error pages, etc.

    ssl_dhparam /etc/ssl/certs/dhparam.pem;

    ssl_certificate /etc/letsencrypt/live/;
    ssl_certificate_key /etc/letsencrypt/live/;

    ssl_session_cache shared:le_nginx_SSL:1m;
    ssl_session_timeout 1440m;

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;


Almost there, next step is obtaining Let's Encrypt certificate.

To receive a certificate from Let’s Encrypt certificate authority (CA), you must pass a challenge to prove you control each of the domain names that will be listed in the certificate. A challenge is one of a list of specified tasks that only someone who controls the domain should be able to accomplish, such as:

  • Posting a specified file in a specified location on a web site (the HTTP-01 challenge)
  • Posting a specified DNS record in the domain name system (the DNS-01 challenge)

I have decided to go with DNS challenge. It was easier for me because I'm hosting my site on Digitalocean hence I was able to use dns-digitalocean plugin that automates the whole process. All I needed to do was to create an ini file with my access token.

# DigitalOcean API credentials used by Certbot
dns_digitalocean_token = 111222333444555666777888999000

sudo docker run -it --rm \
-v /code/docker/services/nginx/ssl/etc/letsencrypt:/etc/letsencrypt \
-v /code/docker/services/nginx/ssl/var/lib/letsencrypt:/var/lib/letsencrypt \
-v /home/user/.secrets/certbot/digitalocean.ini:/tmp/digitalocean.ini \
certbot/dns-digitalocean \
certonly --preferred-challenges dns \
--dns-digitalocean --dns-digitalocean-credentials /tmp/digitalocean.ini \
--dns-digitalocean-propagation-seconds 60 \
--email --agree-tos --no-eff-email \

Last step - DH parameters file.

sudo openssl dhparam -out /code/docker/services/nginx/ssl/dh-param/dhparam.pem 2048

And the final touch - renewing the certificate. I have added below command to my crontab to run everyday.

sudo docker run -it --rm \
-v /code/docker/services/nginx/ssl/etc/letsencrypt:/etc/letsencrypt \
-v /code/docker/services/nginx/ssl/var/lib/letsencrypt:/var/lib/letsencrypt \
-v /home/user/.secrets/certbot/digitalocean.ini:/tmp/digitalocean.ini \
certbot/dns-digitalocean \
renew --preferred-challenges dns \
--dns-digitalocean --dns-digitalocean-credentials /tmp/digitalocean.ini \
--dns-digitalocean-propagation-seconds 60

Now, some explanation how it works:

  • certbot container generates SSL certificate and saves it in mounted host directory
    • DNS challenge is automatically executed by dns-digitalocean plugin
    • plugin creates TXT record required by Let's Encrypt. In order to access Digitalocean API plugin uses access token saved in ini file.
  • host directory used by certbot container is mounted in nginx container
  • certificate and DH parameters paths are used in nginx configuration file to configure SLL certificate

And this is it. In few simple steps I was able to obtain SSL certificate for my containerised nginx server.