HTTPS using Nginx and Let's encrypt in Docker

8 min read

Protecting your website and users using HTTPS is a very common task, and this post will guide you through a step-by-step process to do so in a Docker environment. In order to make the tutorial as simple as possible, I will be using Docker Compose, which provides an infrastructure as code approach.

Nginx as a server

Add the following configuration file into your ./nginx/conf/ local folder. Do not forget to update using your own data.

server {    listen 80;    listen [::]:80;    server_name example.org www.example.org;    server_tokens off;    location /.well-known/acme-challenge/ {        root /var/www/certbot;    }    location / {        return 301 https://example.org$request_uri;    }}

In this configuration,  We explain to nginx that it has to listen to port 80

By default, we want to redirect someone coming on port 80 to the same route but on port 443. That's what we do with the location / block.

Basically, we say \"always redirect to HTTPS except for the /.well-know/acme-challenge/ route\".

We can now reload nginx by doing a rough docker compose restart or if you want to avoid service interruptions (even for a couple of seconds) reload it inside the container using docker compose exec webserver nginx -s reload.

Create the certificate using Certbot

One service is now for nginx, while the other is for Certbot. They may have stated the same volume, as you may have noticed. It's intended to encourage communication between them.

Every user requesting /.well-know/acme-challenge/ will receive a response from nginx on port 80 after Certbot writes its files into ./certbot/www/. Certbot may authenticate our server in this way.

Note that at the end of the volume declaration for Certbot, we used:rw, which stands for \"read and write.\" If you don't, authentication will fail since it won't be able to write to the folder.

docker compose run --rm certbot certonly --webroot --webroot-path /var/www/certbot/ --dry-run -d example.org may now be used to verify that everything is functional. A success message along the lines of \"The dry

Certbot create the certificates in the /etc/letsencrypt/ folder. Same principle as for the webroot, we'll use volumes to share the files between containers.

version: '3'services:  webserver:    image: nginx:latest    ports:      - 80:80      - 443:443    restart: always    volumes:      - ./nginx/conf/:/etc/nginx/conf.d/:ro      - ./certbot/www:/var/www/certbot/:ro      - ./certbot/conf/:/etc/nginx/ssl/:ro  certbot:    image: certbot/certbot:latest    volumes:      - ./certbot/www/:/var/www/certbot/:rw      - ./certbot/conf/:/etc/letsencrypt/:rw

Restart the container using docker compose restart. Nginx should now have access to the folder where Certbot creates certificates.

However, this folder is empty right now. Re-run Certbot without the --dry-run flag to fill the folder with certificates:

$ docker compose run --rm  certbot certonly --webroot --webroot-path /var/www/certbot/ -d example.org

Now that we have those certificates, all that is required is to configure port 443 on nginx.

server {    listen 80;    listen [::]:80;    server_name example.org www.example.org;    server_tokens off;    location /.well-known/acme-challenge/ {        root /var/www/certbot;    }    location / {        return 301 https://example.org$request_uri;    }}server {    listen 443 default_server ssl http2;    listen [::]:443 ssl http2;    server_name example.org;

client_max_body_size 0;
ssl_certificate /etc/nginx/ssl/live/example.org/fullchain.pem; ssl_certificate_key /etc/nginx/ssl/live/example.org/privkey.pem;

location / { \t# ... }}

Renewing the certificates

The certificates for Let's Encrypt and Certbot only last three months, which is a minor drawback. If you don't want users to be blocked by a hideous and frightening notice on their browser, you'll need to renew the certificates you use on a regular basis.

However, now that we've set up this Docker environment, it's simpler than ever to update the Let's Encrypt certificates!

$ docker compose run --rm certbot renew

It only takes a single \"renew\" command to get your system operating as it should. It just needs to be used once every three months. Even automating this procedure