Traefik as Reverse Proxy for Docker Services
Hello and Welcome,
If you want to have multiple hostnames mapping to different services on a single host, you need to setup a reverse proxy somewhere. The job of this reverse proxy would be to map an incoming request with a specific address to a single service (within many) that is running on the host.
I have a Linux host with docker installed, and would like to host a webpage for a friend (friendspage.com
). But I also may host my personal page on the same host, with a different URL, so I need to somehow be able to arrange this in the best way possible.
In comes docker, docker-compose, Traefik.
Requirements
- A Linux Host with docker and docker-compose installed
- A DNS entry pointing to your host — you can buy purchase this from a DNS registrar, this will not be explained here
- Firewall ports open for 80 and 443 — this is very dependent on your setup, this will not be explained here
Let’s host a bloging platform, called ghost.
Traefik is a very easy reverse proxy that I’ve come across while looking into options. When researching, google gives you the usual and well known NGINX, the newer Envoy, or Apache if you’d like to use a tool that’s been around longer.
Each of these have their pros and cons, NGINX and Apache has been around for much longer, and has been battle tested, but unfortunately they don’t offer the flexibility that I need that is offered by Traefik.
So let’s begin. I would like to use docker to run the webpage for my friend to make it easier for me to manage, and I’ll deploy Traefik also within a docker container. I want to have a logical filesystem structure that would be straightforward, and easy to extend. Also, I want separation. If I want to deploy a new webpage, I do not wish to change anything from the existing services.
Traefik Setup
Filesystem:
So we have two folders — one for running traefik, and another for running our webpage for our friend. We’ll get to the contents of the folders in a second, but notice that we have two docker-compose.yml
files here - again one for ghost and one for Traefik. This makes sure that when we need a new service or webpage, we create a new folder and a new docker-compose.yml
file, and all the other services will keep working happily.
Now let’s look at the contents of our folders. Let’s start with Traefik:
This file is rather straightforward. We define the version of the file, then define only one service called traefik
, the image is traefik:latest
, and we provide a command which specifies it to use --docker
to discovery services, and to enable the API using --api
. We open some ports - 80 and 443 for webpage traffic, and 8080 for the dashboard of Traefik. We also mount the /var/run/docker.sock
into the Traefik container so it can read the docker containers of the host, and we mount ./traefik.toml
as config for Traefik and ./acme.json
for Let's Encrypt auto-generated SSL certificates.
There are two important bits here:
networks: public:
to define the network andnetworks: - public
to bind Traefik to this network - this docker network will host services that will be exposed to the outside, and every service that would need to be publicly accessible needs to connect to this networklabels: - "traefik.enable=false"
- Labels are the main way of controlling Traefik config. Through the labels, we can dynamically define new services, with new URLs, without restarting Traefik or any other dependent service
Now let’s take a look at our traefik.toml
file:
There isn’t much in this file. Disable debugging, set log level, define default entrypoints as HTTP and HTTPS, etc.
Important bits:
- We route HTTP to HTTPS with
[entrypoint.http.redirect] entryPoint = "https"
watch=true
enables docker watchingexposedByDefault = false
is important to not expose any backend services such as the DB. You can set this totrue
, and disable within the service definitions.[acme]
block - this is used by Let's Encrypt. Traefik has integrated Let's Encrypt auto-obtained certificate support. In this block, we specify our e-mailemail = "veryniceemail@veryniceemailhosting.com"
, where to store obtained certsstorage = "acme.json"
, define the entry point asentrypoint = "https"
, and request a cert for each Traefik host withonHostRule = true
Let’s take a look at our ghost blog platform setup.
Ghost Setup
docker-compose.yml:
Again, some parts are not important so I’m going to skip over them, and some parts are crucial. Note that all the ${MYSQL_*}
values are defined in a .env
file, which will be picked up by docker-compose
by default.
services.ghost.labels
- This is where we define all the rules for Traefik. Note we havetraefik.enable=true
- To enable Traefik to process this service, and expose ittraefik.docker.network=traefik_public
- Specify which docker network will be used for Traefiktraefik.backend=ghost
- Specify what is the name of the serviec for Traefik to route requests totraefik.frontend.rule=Host:friendspage.com
- Specify the URL to access this servertraefik.port=2368
- Specify the port to route to (ghost uses 2368 by default)service.networks
andnetworks.traefik_public
asexternal: true
- You need to define this for Traefik to be able to route your requests to your container. The otherprivate
network is needed for ghost to connect to MariaDB
This is the main setup. Using this, you’ve setup your friend’s webpage. Now create an account for them, and let them blog away!
Adding Another Service/Host
Now I want a page of my own as well. My friend shouldn’t be the only one having fun, right?
We’re going to create a new folder, copy our ghost/docker-compose.yml
into this folder, and edit it to our needs. Let's use ghost again for our purposes. Here's how it will look like:
Filesystem:
ghost2
is our new website. Let's look at the files there.
IMPORTANT: Only 4 lines changed from the initial ghost deployment. We changed the URL for Traefik and Ghost, and changed the name of the deployment, just to make sure. docker-compose
makes sure that we have different names because we are in different folders (ghost
vs ghost2
), but I would like to be able to tell where I am only by looking at the docker-compose file as well.
So there you have it. A very easy way to get services reverse-proxied, using Traefik, docker, docker-compose, and whatever service you have in the back.
Links
- Traefik Basics: https://docs.traefik.io/basics/
docker-compose
anddocker-compose.yml
reference: https://docs.docker.com/compose/compose-file/- Ghost blogging platform: https://ghost.org/
- DockerHub Traefik: https://hub.docker.com/_/traefik
- DockerHub Ghost: https://hub.docker.com/_/ghost/
- DockerHub MariaDB: https://hub.docker.com/_/mariadb/