DEV Community

Mr Shanas
Mr Shanas

Posted on • Originally published at mrshanas.com on

HTTPS on Docker Containers using Nginx and LetsEncrypt

Introduction

By default, when docker containers are deployed they run on normal HTTP but most times it's better to run web services using HTTPS which is a secure protocol over the internet. So we're going to see how to enable an SSL certificate on docker containers using LetsEncrypt and Certbot

Prerequisites

Certbot requires a live domain for it to be assigned an SSL certificate to it, you can obtain a domain at your chosen registrar, In this article, I'll be using my own domain mrshanas.com. I've created a subdomain for it.

Also, a virtual server is required with docker and docker-compose installed in it, I'm using Digitalocean droplets that have docker installed in it from the market place but you can use any of your choices but make sure the above-mentioned are installed

Getting Started

Create a simple project with a Dockerfile and docker-compose.yaml files and a config directory that contains an empty nginx.conf .

Dockerfile


FROM nginx:1.21.1-alpine
RUN rm /etc/nginx/conf.d/default.conf
COPY ./nginx.conf /etc/nginx/conf.d



Enter fullscreen mode Exit fullscreen mode

The Dockerfile contains a script to pull the Nginx image, delete the default config file, and then copy the defined config file from our project

nginx.conf


server {
    listen 80;
    listen [::]:80;

    server_name test.mrshanas.com;
    server_tokens off;

    location /.well-known/acme-challenge/ {
        allow all;
        root /tmp/acme-challenge;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}



Enter fullscreen mode Exit fullscreen mode

This will enable Nginx to run locally at HTTP, and listen on port 80, and all incoming requests will be redirected to https.

the line listen [::]:80 means for IPv6 addresses.

Change the server name to the domain you want to enable the certificate. For me, it is test.mrshanas.com

Certbot makes a challenge to the server by making a request to the special URL ${your_domain}/.well-known/acme-challenge/ so we want to enable it to access the URL and verify the challenge for it to generate the certificate

docker-compose.yml


version: "3"

services:
  nginx:
    container_name: "nginx_server"
    build:
      context: ./config
    ports:
      - 80:80
      - 443:443
    volumes:
      - ./config:/config
      - /etc/letsencrypt:/etc/letsencrypt:ro
      - /tmp/acme-challenge:/tmp/acme-challenge
    networks:
      - app
    restart: always

  letsencrypt:
    container_name: "certbot"
    image: certbot/certbot
    command: sh -c "certbot certonly --webroot -w /tmp/acme-challenge/ -d test.mrshanas.com --text --agree-tos --email shanas@mrshanas.com --rsa-key-size 4096 --verbose --keep-until-expiring --preferred-challenges=http"
    entrypoint: ""
    volumes:
      - "/etc/letsencrypt:/etc/letsencrypt"
      - "/tmp/acme-challenge:/tmp/acme-challenge"
    environment:
      - TERM=xterm

networks:
  app:
    driver: bridge



Enter fullscreen mode Exit fullscreen mode

The above file defines two docker containers nginx and letsencrypt that will make the task successful.

Running Containers on HTTP

The Nginx container is based on the Dockerfile we created and exposes ports 80 and 443 and volumes that will contain the generated SSL certificates

The certificates will be stored in /etc/letsencrypt

Now run docker-compose up --build nginx and visit your domain name and If it's successful you will see like below

That means the container ran but it is not running on HTTPS, so let's see how to make it secure.

The letsencrypt container pulls the certbot image from docker hub and runs the command certbot certonly --webroot -w /tmp/acme-challenge/ -d test.mrshanas.com --text --agree-tos --email shanas@mrshanas.com --rsa-key-size 4096 --verbose --keep-until-expiring --preferred-challenges=http

Make sure to replace the domain name with the one you want to configure SSL and the email with your email

Generating SSL Certificates with certbot image

Open a new terminal while the Nginx container is running and run docker-compose up --build letsencrypt , If it's successful you will see the output below

That means certbot was successful and generated certificates that are stored in the directory etc/letsencrypt/live/<your_domain> .

Enabling HTTPS on the domain

Add the following lines below the server block in the nginx.conf



server {
    listen 443 ssl;
    listen [::]:443 ssl http2;

    server_name test.mrshanas.com;

    ssl_certificate /etc/letsencrypt/live/test.mrshanas.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/test.mrshanas.com/privkey.pem;
}



Enter fullscreen mode Exit fullscreen mode

Replace test.mrshanas.com with your domain whose the certificate belongs to, and stop the Nginx container then rebuild it again with the command docker-compose up --build -d nginx

-d flag means the container will run in detached mode

If the above command was successful then navigate to your domain and you should see as below

That means the process was successful and your domain is running on HTTPS.

Conclusion

With the configuration, you can deploy your projects using Docker and make them run on HTTPS with ease. You can find the complete sample code in my GitHub repo.

Top comments (4)

Collapse
 
frankrsc profile image
Francisco Flores

Hi, reading your article I wonder if it can also work for a reverse proxy? I've been trying and I can't

Collapse
 
mrshanas profile image
Mr Shanas

Yes it can please can you tell me what's not working

Collapse
 
slidenerd profile image
slidenerd

your article s missing one major thing. when you first run nginx there are no letsencrypt certificates and nginx server wont start without those but to get those certificates, you need nginx server running. how do you workaround this egg and chicken problem?

Collapse
 
oldtechaa profile image
oldtechaa • Edited

Great writeup and I found it really helpful, thanks for posting this! I originally thought that having LE as a separate container would be too much complexity.