Traefik as Reverse Proxy for Docker Services

Aral Can Kaymaz
5 min readJun 9, 2019
Image courtesy of https://traefik.io/

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 and networks: - 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 network
  • labels: - "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 watching
  • exposedByDefault = false is important to not expose any backend services such as the DB. You can set this to true, 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-mail email = "veryniceemail@veryniceemailhosting.com", where to store obtained certs storage = "acme.json", define the entry point as entrypoint = "https", and request a cert for each Traefik host with onHostRule = 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 Traefik traefik.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 and networks.traefik_public as external: true - You need to define this for Traefik to be able to route your requests to your container. The other private 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

--

--