Published by Weisser Zwerg Blog on
An extensible set-up of traefik as reverse proxy via ansible, systemd and docker compose with automatic Let's Encrypt SSL certificates.
You can find the code on GitHub.
In one of my last blog posts, Fuel Save Alerter, I described how to create the set-up on a
netcup virtual private
server (VPS). I was thinking about putting a web-application front-end on top of it, but then I was thinking, what will happen if this will not be the
only web-application I will want to deploy on this VPS? In the end, only one web-application can listen on port 80.
After looking into the topic a bit longer I decided for a set-up with traefik as reverse proxy that handles the incoming
requests, the SSL certificate procurement, the SSL termination and the basic access
authentication. In addition, the set-up is modular and extensible in the sense that a new web-app can
be deployed via docker or docker-compose plus some attached labels and
traefik will pick-up the configuration automatically.
In principle all of this is rather straight forward and the documentation of
traefik is really good, but the configuration and configuration syntax
need some getting used to it. An example will definitely help! Therefore, I’ll show you the details below in the blog post body.
The solution will consist of the following ingredients:
> ANSIBLE_CONFIG=environments/prod/ansible.cfg ansible-playbook -i environments/prod 00-basebox/setup.yml --ask-become-pass
This post assumes that this basic set-up is present.
You will have to adapt the
environments/prod/hosts.yml to your situation. After that you should be able to install this showcase set-up:
> ansible all -m ping > ansible-playbook setup.yml --ask-become-pass
After that you should be able to access the following links (adapted to your case):
You should notice that while you requested a
http URL you’ll be automatically redirected to
https. In addition, you should notice that without any
additional ado you’ll have a valid SSL certificate and the browser does not complain. When you access the second
web2 link it should be protected
via basic access authentication. You can access this page with the user name
The relevant configuration files are in ./roles/traefik/templates.
To show what I mean with modular and extensible I’ve added two examples in the associated Makefile.
> make run_web3
> make run_web4
In both cases a new nginx process will start-up and serve the path
web3 example is raw docker and shows what setting to provide for the
--network options and the different labels.
web4 example uses another separate docker compose file
to show how to deal with the labels and network settings. I wanted to point out that this docker file uses another network name, rather than
default. It uses
main and references
traefik_default as external docker network. When I tried to use
default, it was working the first time I
started that docker compose file, but when I stopped and restarted it did not come up again. Not quite sure why, though.
So the above should give you a flavour of what you’ll get from this set-up: a modular and extensible reverse proxy that’ll handle the SSL certificate procurement and basic access authentication if wanted. So, for any future project you might have, you simply focus on the raw web application, and when finished you deploy it via a docker container with the right labels to make it available via a URL path on your VPS server.
Let’s look a bit more into the details of
traefik. While the project is well documented it took me some getting used to its configuration format and how it all plays together.
traefik listens to incoming traffic via
entryPoints. In our case for
http on port and
https on port . The configuration may look like this:
On the other side there are the real services, like the web applications you write.
traefik calls them
services. The configuration may look like this:
- url: "http://web1_nginx:80"
In between happens routing via
routers and “cross cutting concerns” via
middlewares. So the whole processing chain looks like:
entryPoint -> router -> [middleware ->]* service-> real service
The above notation means that there are 0 or several middlewares involved.
There are so called
providers that are responsible for providing the
required configuration. While you’re getting started with
traefik I recommend you stick to the
provider, because it makes things most clear
and transparent. To achieve modularity and extensibility you’ll have to use the
provider, which listens on the docker socket for
container events and will process the attached labels to gather the required configuration.
In order to see how
traefik sees the world the
traefik dashboard is really useful. You can access it by port forwarding port from your VPS to your
> ssh -L8081:localhost:8081 email@example.com
and accessing it via the URL:
The dashboard is automatically enabled if you add
It will listen on an
traefik, which will be auto-created if it is not present in your config file. If you want to change its
default port of you’ll have to add it to your configuration explicitly.
One thing that puzzled me at the beginning was how the “things” are named. They are named automatically by the position in the configuration
hierarchy, e.g. a name of a service is the thing behind
XXX) part. Similar for
http.routers.XXX. If you want to reference a named “thing” from the configuration of a different
provider you have to add as a postfix the name
of the provider, e.g.
@file. Here is for example how the
web2 router and the
web2strip middleware are defined via docker labels:
- traefik.http.routers.web2.rule=( Host(``) && PathPrefix(`/web2`) )
Pay attention to the
traefik.http.routers.web2.middlewares router configuration, where two middlewares are referenced. The first one was defined in
the same provider and therefore does not carry y postfix. It is just called
web2strip. The second one was defined via the
therefore required the postfix. It is called
Most of the time you want the
traefik reverse proxy to terminate the TLS connection. You must configure at least one (sub-)property of the
configuration to achieve this. See the routers/#tls documentation for details.
When you configure
services you may have to name the “hosts” where they run on, e.g.:
- url: "http://web1_nginx:80"
Here the service URL contains a host name
web1_nginx. This is coming from the docker file, because you’re inside the docker compose
network. Sometimes it is confusing to think about what a host name refers to. I strongly advise against using
localhost as this is most
confusing. It means something different inside a container and on the VPS outside the container.
traefik is supposed to support
http2 was straight forward to configure and it worked on first attempt. I do not know what I
did wrong with
http3. In my opinion, the current configuration should work, but it doesn’t.
If you want to add user names and passwords to the
basic access authentication use the
> htpasswd -n cs224
In case you ever wanted to get rid-off the complete installation follow the below:
vagrant> sudo su root> systemctl stop traefik root> systemctl disalbe traefik root> systemctl daemon-reload root> systemctl list-units --all | grep traefik # -> should be empty root> rm /etc/systemd/system/traefik.service root> cd /opt/traefik root> docker compose down -v --remove-orphans # --rmi all root> cd .. root> rm -rf /opt/traefik