Add HTTPS support for your backend API in 5 mins with traefik as a reverse proxy and docker

May 24, 2020

The way I deploy my apps is usually:

  1. deploy the frontend app to managed services like Netlify or Zeit, depending on if I'm using Next.js
  2. deploy the backend service to a VPS provider such as DigitalOcean(get $100 free credit via my link) or Vultr ( provider with the best performance)

Most frontend deployment services like Netlify and Zeit have built in https support so enabling https for the frontend has never been an issue for me. However, because the frontend uses https, it forces the backend to be as secure, meaning you cannot make http request to fetch data.

I have tried to use nginx and letsencrypt to set up https support for my API service in the past, but I felt it was overly complicated for just a small application. Luckily I found a very simple and easy alternative to nginx, traefik.

traefik allows you to set up a reverse proxy with nothing but a docker-compose file! And it also makes enabling https support very easy. How amazing is that? So let me walk you through how to deploy you backend API with traefik in 5 minutes.


Before we start, you need:

  • a domain name
  • docker installed
  • a docker image of your backend API (See my previous post)


I'll just provide very simple steps on how to set up your service. If you'd like to learn more about the configuration, here's the official document:

  1. Create a docker-compose.yml file and copy paste the content below
  2. Replace jameskutw/api with your own docker image
  3. replace with your own domain
  4. make sure the port mapping for your API container is correct

Basically traefik gets information from the labels of a container. As you can see in the file. For example "traefik.http.routers.api.rule=Host(``)" tells traefik that traffic from should be directed to the api container.

version: "3.3"


    image: "traefik:v2.2"
    container_name: "traefik"
      #- "--log.level=DEBUG"
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.myresolver.acme.httpchallenge=true"
      - "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web"
    #   - "--certificatesresolvers.myresolver.acme.caserver="
      - ""
      - ""
      - "80:80"
      - "443:443"
      - "8080:8080"
      - "./letsencrypt:/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"

    image: "jameskutw/api"
    container_name: "api"
        - "5001:80"
      - "traefik.enable=true"
      - "traefik.http.routers.api.rule=Host(``)"
      - "traefik.http.routers.api.entrypoints=websecure"
      - "traefik.http.routers.api.tls.certresolver=myresolver"

Because Let's encrypt(free ssl certificate provider) has a cap on how many times you can request a certificate, to do a dry run just to see if it works, uncomment this line "--certificatesresolvers.myresolver.acme.caserver=" and run docker-compose up -d

If everything works fine

  1. remove the containersdocker-compose down
  2. comment back that line you uncommented
  3. remove letsencrypt/acme.json
  4. run the containers again docker-compose up -d

Subscribe to my email list