May 8, 2024
14 Min read

Getting started with Traefik on Docker

In this tutorial, we will deploy Traefik cloud-native application proxy on Docker.

Traefik cloud native application proxy

Prerequisites

You can do this tutorial on Linux, Mac, or Windows workstation.

Alternatively, you can do this on a virtual server created with Multipass which is a quick and easy tool to run Ubuntu virtual servers on Linux, Mac, or Windows.

Outline

  1. Background: What is Traefik
    1. What makes Traefik cloud native
  2. Deploying Traefik on Docker
    1. Prepare Docker Host (virtual server)
    2. Prepare Docker Host (Linux, Mac, or Windows workstation)
    3. Launch Traefik container with Docker CLI
    4. Launch Traefik container with Docker Compose
  3. Basic reverse proxy with Traefik
  4. Loadbalancing HTTP requests across multiple replicas with Traefik
  5. Routing HTTP requests to multiple backend services with Traefik
  6. Wrap up

Background: What is Traefik

Traefik is a Cloud Native Application Proxy. The Traefik documentation also calls Traefik an Edge Router. But, the terms Cloud Native Application Proxy or Edge Router may mean different things to different people.

To understand what is Traefik, let’s see what Traefik can do.

Traefik sits in front of your web application(s) and receives HTTP requests on behalf of the applications. Traefik then decides where to route the HTTP requests, sends and receives the responses from the backend application, and sends the responses to the user.

Traefik revese proxy

So… Traefik is a reverse proxy.

But Traefik differs from traditional reverse proxy servers because Traefik is cloud native.

What makes Traefik cloud native

Traefik has two types of configurations:

  1. Static configurations
  2. Dynamic configurations

Static configurations - as you may have already guessed - are fairly static. There are two types of static configurations:

  1. Entrypoints: An entrypoint is the IP + port that Traefik listen for incoming HTTP requests.
  2. Provider connection information: A provider is a source that provides dynamic configurations to Traefik. Docker, Kubernetes, Redis, an HTTP API, etc., can serve as providers. The provider connection information tells Traefik how to connect with the provider to get the dynamic configurations.

You can do the static configurations via:

  1. Configuration file
  2. CLI
  3. Environment variables

Dynamic configurations control how Traefik routes incoming requests to backend applications. Traefik gets the dynamic configuration from the provider.

There are four types of dynamic configurations in Traefik.

  1. Routers: Routers send incoming HTTP requests to Services, get the response, and send it back to the client. Routing configuration defines the parameters for selecting a service based on the parameters of the incoming HTTP requests.
  2. Services: Services are web applications or APIs that serve HTTP requests. The Services configuration defines the URL+port for how to reach a service.
  3. Middlewares: Middlewares tweak HTTP requests and responses between Entrypoints and Services.
  4. Certificates: Certificates configure the SSL certificates and keys.

The provider updates Traefik dynamic configurations according to state changes in the web app(s) behind Traefik. So, Traefik is well suited as a cloud native reverse proxy for cloud-native applications that change status frequently.

Deploying Traefik on Docker

We are going to launch a Traefik container instance on our Docker Host - Linux, Mac, or Windows workstation or the virtual server created with Multipass.

Let’s prepare the Docker Host first.

Prepare Docker Host (virtual server)

  1. Launch virtual server with Multipass:
    $ multipass launch -c 2 -m 2G -d 10G -n docker-host
    

    This creates a virtual server docker-host with 2 vCPUs, 2G memory, and 10G of disk space using the latest Ubuntu LTS OS version.

  2. Login to the virtual server
    $ multipass shell docker-host
    
  3. Install Docker Engine by following the installation instructions

Prepare Docker Host (Linux, Mac, or Windows workstation)

Install Docker Desktop by following the installation instructions for your platform

Launch Traefik container with Docker CLI

Open or login to the terminal on the Docker Host. If your Docker Host is a Windows workstation, open the Windows PowerShell.

Launch Traefik container instance:

$ docker container run -d -p 80:80 -p 8080:8080 --name traefik traefik:v3.0 --api.insecure=true 

The --api.insecure=true enables the Traefik dashboard.

Access the Traefik dashboard by opening http://<docker-host>:8080 in your browser.

Replace <docker-host> with localhost if your Docker Host is your workstation. If your Docker Host is a virtual server, replace <docker-host> with the virtual server IP address.

The Traefik dashboard shows all static and dynamic configurations of your Traefik instance.

Traefik dashboard without Docker provider

On the first row, we get the two entrypoints HTTP and TRAEFIK that are created by default.

The TRAEFIK entrypoint listens on port 8080 which runs the Traefik dashboard we are accessing right now. The HTTP entrypoint listens on port 80.

We get the dynamic configurations for Routers, Services, and Middleware on the second row.

Click on Explore on each widget and check the configurations. The Routers, Services, and Middleware currently here are configured by Traefik itself for the Traefik dashboard.

Launch Traefik container with Docker Compose

To use Traefik as a reverse proxy for web applications on Docker, we must enable Docker provider by supplying Traefik with relevant parameters at launch.

But it’s inconvenient to type in so many parameters in the Docker CLI. So let’s use Docker Compose which is a tool for running containers on a Docker host using YAML definitions. Docker Compose is included in Docker Desktop and Docker Engine so you don’t have to install anything new.

A YAML file used by Docker Compose is known as a compose file. We can succinctly define any number of parameters in the compose file.

Docker Compose expects a compose file to be named compose.yml. Let’s create a compose file.

Create directory traefik on the Docker Host:

$ mkdir traefik
$ cd traefik

Create compose.yml inside the traefik directory:

# compose.yml

version: '3'

name: web-app
services:
  traefik:
    image: traefik:v3.0
    command: 
      --api.insecure=true 
      # Enable Docker provider
      --providers.docker
    ports:
      - "80:80"
      - "8080:8080"
    # Attach the Docker volume so Traefik can listen to Docker events and update the dynamic configurations
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock

Delete existing Traefik container:

$ docker container stop traefik
$ docker container rm traefik

Deploy Traefik from the compose file:

$ docker compose up -d

List the runnning containers:

$ docker container ls
CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS         PORTS                                                                          NAMES
4b5009df305d   traefik:v3.0   "/entrypoint.sh --ap…"   3 seconds ago   Up 3 seconds   0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp   web-app-traefik-1

Open Traefik dashboard at http://<docker-host>:8080 on your browser. Scroll to the bottom and confirm that Docker is updated as a provider.

Also, there are three Routers created now. Click on Explore on the Routers widget and inspect the third router which is created by the Docker provider.

Traefik Routers with Docker provider

This route sends all incoming HTTP requests on port 80 to the Traefik instance so Traefik is ready to route HTTP requests to our web apps.

Basic reverse proxy with Traefik

For Traefik to work as a reverse proxy:

  1. A web app must be up and running on our Docker Host.
  2. Docker must update Traefik dynamic configurations to route HTTP requests to our web app

Let’s use the simple and functional traefik/whoami as the web app. We’ll use Docker lables so Docker can update Traefik dynamic configurations for this web app.

Update the compose file:

# compose.yml

...

services:
  traefik:
    ...

  # Add second service to the compose file
  whoami:
    # whoami image
    image: traefik/whoami
    # Add a label to the container.
    labels:
      # Traefik dynamic configuration. 
      - "traefik.http.routers.whoami.rule=Path(`/`)"

The label traefik.http.routers.whoami.rule=Path(/) creates a Traefik Router that routes all HTTP requests with target path / to our web app.

Here’s the generic format of a label to create a router.

traefik.<entrypoint>.routers.<router-name>.rule=<match-condition>

Launch the new container:

$ docker compose up -d

This command creates the new whoami container without impacting the existing traefik container.

Confirm that whoami is running:

$ docker container ls

Check the Traefik dashboard on your browser. The number of routers is four now.

Click on the Explore button in the Routers widget to get the list of Routers.

Traefik has created a new router named whoami@docker from the label we added to the whoami container in the compose file. The router name is derived as <router-name-in-the-label>@<provider-name>. Traefik dashbord with four routers

On the Docker host, test whoami service with curl:

$ curl localhost
Hostname: a1d5d4c1f063
IP: 127.0.0.1
IP: 172.19.0.2
RemoteAddr: 172.19.0.3:51242
GET / HTTP/1.1
Host: localhost
User-Agent: curl/7.81.0
Accept: */*
Accept-Encoding: gzip
X-Forwarded-For: 172.19.0.1
X-Forwarded-Host: localhost
X-Forwarded-Port: 80
X-Forwarded-Proto: http
X-Forwarded-Server: ecd0d4cf1848
X-Real-Ip: 172.19.0.1

whoami responds with the Hostname and IP address of the container instance that it’s running on, and the parameters it received in the HTTP request.

Next, try to access whoami API at /api:

$ curl localhost/api
404 page not found

What’s wrong here?

This rule we created matches only the exact path so will not match /api path.

traefik.http.routers.whoami.rule=Path(`/`)

Let’s update the rule to match a prefix using the Traefik rule condition PathPrefix.

# compose.yml
...
  whoami:
    image: traefik/whoami
    labels:
    # Use PathPrefix rule condition instead of Path.
      - "traefik.http.routers.whoami.rule=PathPrefix(`/`)"

Check out the complete list of Rule conditions in Traefik docs.

Update the deployment:

$ docker compose up -d

Traefik will update the rules automatically by listening to Docker events. So the above command will update the Traefik router whoami@docker which you can check on the Traefik dashboard.

Test again with curl:

$  curl localhost/api
{"hostname":"4c31a8459af0","ip":["127.0.0.1","172.19.0.2"],"headers":{"Accept":["*/*"],"Accept-Encoding":["gzip"],"User-Agent":["curl/7.81.0"],"X-Forwarded-For":["172.19.0.1"],"X-Forwarded-Host":["localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["ecd0d4cf1848"],"X-Real-Ip":["172.19.0.1"]},"url":"/api","host":"localhost","method":"GET","remoteAddr":"172.19.0.3:46054"}

Traefik is correctly routing the request to the whoami backend service and whoami is sending a JSON response now.

Loadbalancing HTTP requests across multiple replicas with Traefik

On the Traefik dashboard home page, click on Explore in the Services widget.

Traefik dashbord Services list

Click on the whoami-web-app@docker Service. We have only one replica (container instance) in this service. Let’s create a second replica to share the load.

Update the whoami Service in compose.yml

# compose.yml
...

  whoami:
    image: traefik/whoami
    labels:
      - "traefik.http.routers.whoami.rule=PathPrefix(`/`)"
    deploy:
      mode: replicated
      # Create two replicas of the whoami container.
      replicas: 2

Update deployment:

$ docker compose up -d

Traefik will update the new Replica in whoami-web-app@docker Service.

Traefik dashbord Service with two replicas

Also, Traefik will start load sharing among the available replicas which we can verify by sending multiple requests with curl.

$ curl localhost | grep IP:
IP: 127.0.0.1
IP: 172.19.0.4
$ curl localhost | grep IP:
IP: 127.0.0.1
IP: 172.19.0.2

Routing HTTP requests to multiple backend services with Traefik

A web application often consists of more than one backend service. Traefik can route HTTP requests to multiple backend services based on Rule conditions Traefik docs and can also manipulate the requests with Traefik Middlewares.

Let’s consider a typical web application with two backend services whoami-a and whoami-b. Incoming HTTP requests to /whoami-a/* must be routed to whoami-a, and requests to /whoami-b/* must be routed to whoami-b service. Also, we need to remove the /whoami-a/ and /whoami-b/ in the URL before sending the request to the appropriate service.

Traefik routing HTTP to multiple backends

Update the compose file:

# compose.yml

...
  # Change service `whoami` to `whoami-a`.
  whoami-a:
    image: traefik/whoami
    labels:
      # Update the router name as whoami-a and PathPrefix as `/whoami-a/`
      - "traefik.http.routers.whoami-a.rule=PathPrefix(`/whoami-a/`)"
      # Create new middleware to strip prefix /whoami-a from the HTTP request
      - "traefik.http.middlewares.strip-a.stripprefix.prefixes=/whoami-a"
      # Attach middleware to router
      - "traefik.http.routers.whoami-a.middlewares=strip-a@docker"
    deploy:
      mode: replicated
      replicas: 2
  # Add new service whoami-b with router name whoami-b and path prefix `/whoami-b/`.    
  whoami-b:
    image: traefik/whoami
    labels:
      # New router whoami-b with PathPrefix `/whoami-b`
      - "traefik.http.routers.whoami-b.rule=PathPrefix(`/whoami-b/`)"
      # Create new middleware to strip prefix /whoami-b from the HTTP request
      - "traefik.http.middlewares.strip-b.stripprefix.prefixes=/whoami-b"
      # Attach middleware to router
      - "traefik.http.routers.whoami-b.middlewares=strip-b@docker"
    deploy:
      mode: replicated
      replicas: 2

We use a new label to create a Traefik Middleware stripprefix which deletes the specified prefix from the target URL in the incoming HTTP request.

Check out the complete list of HTTP Middlewares in Traefik docs.

The format of the label to create the middleware:

traefik.http.middlewares.<middleware_name>.<type-of-middleware>.<parameter>=<value>

We need another label to attach the middleware to router:

traefik.http.routers.<router-name>.middlewares=<middleware-name>@<provider-name>

Update deployment:

$ docker compose up -d

Remove the orphaned container whoami (since we renamed the service whoami):

docker compose up -d --remove-orphans

Test service whoami-a:

$ curl localhost/whoami-a/
Hostname: 7a9018de57e2
IP: 127.0.0.1
IP: 172.19.0.5
RemoteAddr: 172.19.0.3:44514
GET / HTTP/1.1
Host: localhost
User-Agent: curl/7.81.0
Accept: */*
Accept-Encoding: gzip
X-Forwarded-For: 172.19.0.1
X-Forwarded-Host: localhost
X-Forwarded-Port: 80
X-Forwarded-Prefix: /whoami-a
X-Forwarded-Proto: http
X-Forwarded-Server: 5f52018b39b2
X-Real-Ip: 172.19.0.1

In the output, the target URL in the HTTP requests is /. That’s Traefik middleware in action stripping off whoami-a in the origianl HTTP request.

Try the curl request to localhost/whoami-a/ several times and check out that the requests are loadshared between the two replicas belonging to whoami-a.

Send HTTP request to whoami-b.

$ curl localhost/whoami-b/
Hostname: c798cae6975d
IP: 127.0.0.1
IP: 172.19.0.6
RemoteAddr: 172.19.0.3:41406
GET / HTTP/1.1
Host: localhost
User-Agent: curl/7.81.0
Accept: */*
Accept-Encoding: gzip
X-Forwarded-For: 172.19.0.1
X-Forwarded-Host: localhost
X-Forwarded-Port: 80
X-Forwarded-Prefix: /whoami-b
X-Forwarded-Proto: http
X-Forwarded-Server: 5f52018b39b2
X-Real-Ip: 172.19.0.1

Similar to whoami-a, the target URL is sent to whoami-b after sripping /whoami-b in the URL.

Wrap up

We have demystified the Traefik Cloud Native Application Proxy. Traefik is none other than a reverse proxy that plays well in the cloud-native ecosystem.

So, can I replace my traditional reverse proxy with Traefik?

Yes. You can. Traefik will also automate some boring stuff that you’ve been doing with conf files.

But, there’s a point to note. Most traditional reverse proxy servers are also web servers and can serve static files like CSS or JavaScript. Traefik is a pure application proxy and does not support serving static files. This is not a big deal since most of the time you need to rely on a CDN to serve static assets. Traefik can be your cloud-native application proxy serving the dynamic part of the application.

In this tutorial, we deployed Traefik on Docker. In an upcoming tutorial, let’s check out what Traefik can do on Kubernetes.