Local HTTPS development with Docker compose, Traefik and Mkcert.

A lock on a blue door
A lock on a blue door
Photo by Erik Mclean on Unsplash

Why using HTTPS in development?

The need to use HTTPS in development may seem anecdotal, however, several cases meet this need:

  • To perform certain tests locally (such as lighthouse audits)
  • Avoid CORS errors thanks to reverse proxies
  • To respond to point number 10 of the 12 factor app which suggests that the production and dev environments should be as similar as possible.

How to implement it using docker-compose?

To explain how to implement an HTTPS environment in dev, I will use a small project that will serve as an example throughout this article.

This project is based on 2 microservices:

  • An “API” written in Flask / Python
  • A whoami container (it’s an image based on Go who return info about the host when you request it)

The reverse-proxy I will use for this project is Traefik as described in docs:

Traefik is an open-source Edge Router that makes publishing your services a fun and easy experience. It receives requests on behalf of your system and finds out which components are responsible for handling them.

It’s easy to use and work well with docker-compose.

Finally, the last utility we will use, and it’s the one who will allow us to use HTTPS locally is mkcert. It allows us to create TLS certificates who are locally trusted by a Certificate Authority installed and managed by mkcert itself.

This is what our project tree looks like:

Nothing much to add about it, the main configs file are at the root of the project and the Flask API files got their folder.

To describe how this works we will review each service from the docker-compose.yml:

The docker-compose file describes 3 services:

  • The back-end (Flask application)
  • whoami (The Go based image to return info about the host)
  • traefik (the http/tcp/udp reverse-proxy)

The first service we will review is our extremely complex Flask API. The latter is single-file and consists of a single endpoint that returns “Hello, world” (told you)

main.py:

Nothing much to add, we just have an app running on localhost on port 5000

requirements.txt:

Dockerfile:

This dockerfile is not suited for production because it uses the development server Flask is offering us. For a production grade image, you might want to use an image based on a wsgi server like gunicorn. However, this image is still useful during development, because it allows us to have hot reload of any changes made in our code.

The second service we’ll use is a Whoami docker image, there’s no code to write here, we’ll just have to run a container based on this image.

The third service that is used in our docker-composer is Traefik. The latter will require two configuration files, a “static” config file which will be the startup file of the application and a “dynamic” config file which will define our routes policies.

traefik.toml (static):

traefik.config.toml (dynamic):

Now that we got everything defined, we need one last element to make the stack work with HTTPS locally.

In the Traefik configuration, we defined some TLS certificates to use, to generate those we will simply just use mkcert.

It’s straightforward to install mkcert on your system:

  • On macOS:
  • For Linux & Windows installation, there are some lines in the documentation who cover your OS:

Once mkcert is installed on your computer, there are 3 steps’ to follow:

Install the local CA:

Generate your certificates:

Modify your /etc/hosts by adding this line at the end of the file:

Once all those steps are done, you can run your docker-compose stack:

A terminal with stdout from docker-compose showing the 3 services running.
A terminal with stdout from docker-compose showing the 3 services running.
Hurrah !

Now you should be able to access the Flask API from your web browser using https://foo.bar:

A picture showing a web browser with foo.bar address, also show a secured connexion label.
A picture showing a web browser with foo.bar address, also show a secured connexion label.
The certificat should be emited by mkcert if you click on it.

Now if you access https://foo.bar/whoami you should get some info about your whoami container like this:

And if you access whoami directly with localhost:5001:

Hostnames are the same, it’s the container id you can get by typing:

However, the hosts are different and correspond to either your local TLS certificates domain name or simply your localhost.

To conclude:

This method give you the ability to work in an environment who is closer to your production environment will give you a way to work on your code without building/deploying to a staging/production area every time to verify some implementation where you might need HTTPS.

I’m good at Googling and browsing Stack Overflow

Get the Medium app