Add SSL to every docker-compose setup using traefik in no time

Simon Auer
5 min readDec 30, 2020

We host most of our applications, but also some of our dev tools on dedicated cloud instances.
We have a special setup routine for these and try to save time, whenever we do it.
(I will gladly share our setup routine — let me know if you are interested in an article about that)

Some of these applications have custom ports, that we don’t want to expose, and all of them need SSL support of course.

With the introduction of LetsEncrypt this has gotten a lot easier, but even with nginx and certbot it always took us a while to set everything up correctly.

Traefik to the rescue

This is where traefik proxy comes in. :)

This let’s us host single page applications (react, angular, …), gitlab, … with ssl and port forwarding with no effort.

What do I want to show you today?

5 minute work to add the following features to your docker-compose:

  • SSL / https / secure serving:
    Create SSL certificate using letsencrypt automatically and serve https version of page
  • Force https:
    Make sure that the user always is served the secure version of your page
  • Port forwarding:
    If your application uses a custom port like ReactJS, Angular, Vue, Ruby, … make it available over standard ports

What is traefik?

From their website:

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.

Traefik takes everything from the internet and maps it correctly to your internal services

Basically, what it does is, it takes the configuration you either enter in a toml or directly in the docker-compose.yml file and maps everything correctly.

It can also take care of some other things like SSL certificates, https redirect, … if you want.

I am going to show you how to set this up quickly.

Why use traefik over nginx/certbot?

Both work well and do what they need to do.
The reason why we chose traefik over the latter (where it makes sense of course) is that the traefik setup is basic and fast.
We just copy the following additional lines in to our docker-compose file and customize 2 lines, and we are done. No complex setup of multiple conf files, that need to work together.

Our starting point

Let’s just imagine our current docker-compose.yml file looks as follows:

version: '3'services:
whoami:
image: "myapplication/whoami"
container_name: "myapplication"
restart: unless-stopped
ports:
- "4000:4000"
networks:
- app-network

How to make it awesome

First copy the following block as it is into your docker-compose.yml file below your whoami (or whatever you call it) service.
Only replace your email address, which is used to report problems with renewals.

traefik:
image: "traefik:v2.2"
container_name: "traefik"
command:
# this can be uncommented to get more information, in case something doesn't work
- "--log.level=DEBUG"
# set this to true to get access to the traefik web interface unter http://YOURIP:8080
- "--api.insecure=false"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=true"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.myresolver.acme.httpchallenge=true"
- "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web"
#- "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory" # uncomment this line to only test ssl generation first (to make sure you don't run into letsencrypt limits)
- "--certificatesresolvers.myresolver.acme.email=#####YOUR_EMAIL_ADDRESS######"
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
ports:
- "80:80"
- "443:443"
- "8080:8080" # this is used for the web interface, that let's you check and monitor traefik and your configuration. It's very nice for debugging your config - only available if "api.insecure" above is set to true
volumes:
- "./letsencrypt:/letsencrypt"
- "/var/run/docker.sock:/var/run/docker.sock:ro"
networks:
- app-network
# The following is only necessary if you want to enforce https!
# if you don't need that, you can just remove the labels here
labels:
- "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"
- "traefik.http.routers.http-catchall.entrypoints=web"
- "traefik.http.routers.http-catchall.middlewares=redirect-to-https"
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"

Enable traefik in your application

You have prepared traefik now. The only thing left is to tell traefik to apply it to your application.

The good thing is:
Adding traefik to your application is a breeze

We only have to add the following 4 lines to your application service as labels : (Make sure to replace #####YOUR_DOMAIN##### and #####PORT#####)

nginx:
...
labels:
# this enables traefik for your service
- "traefik.enable=true"
# this defines the url, traefik will get the ssl certificate for - "traefik.http.routers.myapplication.rule=Host(`#####YOUR_DOMAIN#####`)"
# this tells traefik to use https to access the website
- "traefik.http.routers.myapplication.entrypoints=websecure"
# this tells traefik to use the certresolver, that we defined above for resolving tls (in our case letsencrypt)
- "traefik.http.routers.myapplication.tls.certresolver=myresolver"
# this let's us forward the port we set above. Change this to the port you expose in your application (3000, 4000, ...) or remove the line, if your application already exposes port 80/443
- "traefik.http.services.myapplication.loadbalancer.server.port=#####PORT#####"

Run it

Now the only thing left is to run it:

docker-compose up -d

That’s it — we are done

As you can see, it is pretty easy. We always just copy paste the two parts above into our docker-compose file, change email, domain and port and we are done.

Fully working ssl setup, that renews automatically and just works!

Here is the full file:

version: '3'services:
whoami:
image: "myapplication/whoami"
container_name: "myapplication"
restart: unless-stopped
ports:
- "4000:4000"
networks:
- app-network
labels:
- "traefik.enable=true"
- "traefik.http.routers.myapplication.rule=Host(`mydomain.at`)"
- "traefik.http.routers.myapplication.entrypoints=websecure"
- "traefik.http.routers.myapplication.tls.certresolver=myresolver"
- "traefik.http.services.myapplication.loadbalancer.server.port=4000"
traefik:
image: "traefik:v2.2"
container_name: "traefik"
command:
# - "--log.level=DEBUG"
- "--api.insecure=false"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=true"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.myresolver.acme.httpchallenge=true"
- "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web"
#- "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
- "--certificatesresolvers.myresolver.acme.email=webmaster@example.com"
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
ports:
- "80:80"
- "443:443"
- "8080:8080"
volumes:
- "./letsencrypt:/letsencrypt"
- "/var/run/docker.sock:/var/run/docker.sock:ro"
networks:
- app-network
labels:
- "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"
- "traefik.http.routers.http-catchall.entrypoints=web"
- "traefik.http.routers.http-catchall.middlewares=redirect-to-https"
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"

Let me know

Let me know if it works for you, if you have questions or if there is anything else you are interested in. :)

Also follow me here and on twitter for more content regarding devops, react, laravel, flutter and stuff like this. ;)

--

--

Simon Auer

I develop software using modern technologies like Laravel, React, React Native and Flutter. Follow me also on https://twitter.com/SimonEritsch