HTTPS on Docker Containers using Nginx and LetsEncrypt
Published: 2023-01-26 19:55:05

---
title: HTTPS on Docker Containers using Nginx and LetsEncrypt
published: true
date: 2023-01-21 21:19:56 UTC
tags: 
canonical_url: 
---

# 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](https://letsencrypt.org) 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

```

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;
    }
}

```

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

```

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

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1674334545858/3caf3f73-681c-44f1-8f01-18305fa66050.png)

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

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1674334897415/e9d910e1-cab1-4d0c-8561-40f68ab599ca.png)

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;
}

```

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

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1674335412858/986f4af1-fe5a-4ebc-9138-3e8e5561b5ef.png)

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](https://github.com/mrshanas/https-docker).