How To Set Up A Reverse Proxy With Free SSL Using Traefik
Are you looking to set up a reverse proxy for your hosted application and secure it with a free SSL certificate? Look no further! In this comprehensive guide, we’ll take you through the entire process step-by-step using Traefik.
In this four-part series, we already learned how to set up a server (VPS), and we successfully hosted a simple application using Docker, making it accessible through a domain using an A record. The next essential step is to secure our application with SSL. That way, we ensure a safe data transfer between the server and the client. With services like Let’s Encrypt, obtaining an SSL certificate has become a breeze, and it’s free! So, in this post, we will learn how to set up a reverse proxy for your application and how to secure it with free SSL certificates using Traefik. Meaning by the end of this post, you’ll be able to access your application under your domain with HTTPS.
This post has two alternatives
- traefik (this post): a bit more complex but more powerful and automated
- caddy: simple, powerful, but manual
- nginx-proxy and letsencrypt-companion: simple and automated
You can read a comparison here.
Use Traefik as a reverse proxy with free SSL for your application
Now that we set up the domain for the server (If you did not set up a domain yet, do so before continuing. Learn how to do in the last post.), we can use Traefik as a reverse proxy to direct the traffic from a certain domain to a certain container. Traefik automatically generates all the configurations etc., automatically for you. That way, you have to set some labels while creating a new container, and Traefik will take care of the rest for you.
Before we can create new containers using Traefik, we first have to configure it appropriately. Therefore we will create a new directory called traefik, and inside it, create a file called
traefik.yml. This file is the configuration file and holds all the information the container needs:
global: checkNewVersion: true sendAnonymousUsage: false api: dashboard: true # Set insecure to false for production! insecure: true entryPoints: web: address: :80 http: redirections: entryPoint: to: websecure scheme: https websecure: address: :443 http: tls: certResolver: default certificatesResolvers: default: acme: email: <your@email-com> storage: /ssl-certs/acme.json httpChallenge: entryPoint: web providers: docker: exposedByDefault: false
The entry points section defines where with which ports traefik can be accessed. In our case, HTTP (80) and HTTPS (443). In addition, we also added a redirect from HTTP to HTTPS because we only want secured traffic.
The certificates resolver section sets some options needed to request certificates from Let’s Encrypt. Additionally, it defines where the SSL certificates should be stored. We chose
ssl-certs/acme.json , and therefore, we need to create a volume in our container to persist the certificates (if we don’t do this, traefik will request a new certificate on every restart). Inside the HTTPS section of the entry points, we also set the defined certificate resolver as the default one (this saves us a lot of work).
In the last section, the providers, we enable docker so that traefik listens on docker events. Additionally, we set exposedByDefault to false so that we have to enable the containers we want to be affected by traefik manually using the enable label. You can learn more about the different configuration options here.
We then need to modify the
docker-compose.yml file from the last post. Here we create a new container called traefik and add the config file as a volume to the container. Because we place it in the path
/etc/traefik/ the container is able to pick it up automatically.
traefik: image: traefik:v2.9 ports: - "80:80" - "443:443" # remove port 8080 for production - "8080:8080" volumes: - /var/run/docker.sock:/var/run/docker.sock - ./traefik/traefik.yml:/etc/traefik/traefik.yml - ./traefik/ssl/:/ssl-certs/
In addition to adding the traefik container, we also need to configure our application container using labels. The labels we have to set are:
traefik.enable=true: it allows traefik to track events of this container.
traefik.http.routers.<service-name>.rule=Host(`<domain.tld>`): specifies the domain we want to use for this container.
traefik.http.services.: specifies the port the traffic should be directed to (Only needed if multiple ports are exposed by the container).
The final file looks like this:
services: traefik: image: traefik:v2.9 ports: - "80:80" - "443:443" # remove port 8080 for production - "8080:8080" volumes: - /var/run/docker.sock:/var/run/docker.sock - ./traefik/traefik.yml:/etc/traefik/traefik.yml - ./traefik/ssl/:/ssl-certs/ frontend: container_name: frontend image: ghcr.io/<username>/<image-name>:latest labels: - "traefik.enable=true" - "traefik.http.routers.frontend.rule=Host(`<domain.tld>`)"
You can now run both containers using
docker compose up -d traefik will then pick up the running frontend container and add a configuration for it. It will also create a new SSL certificate inside the SSL volume, which you can find inside of your
traefik/ssl folder outside the container.
You can also check if everything is working as expected by going to the
<public-ip:8080> and inspecting the traefik dashboard. If everything works as expected you should remove the 8080 mapping of your container and disable the insecure option inside the
traefik.yml file. Afterward, rebuild the container by running
docker compose up -d traefik.
You can now access your application under
<domain.tld>, and the traffic is secured using SSL.
In this series of posts, we learned how to set up a server (VPS), how to host our first application using docker, and lastly, how to set up a domain and secure it using a reverse proxy with free SSL certificates. Our application is now reachable through https and our chosen domain!
I hope these posts were helpful to you. If so, share them with your friends, and let me know if you have questions!
In case you liked this consider subscribing to my newsletter and joining my discord community!