Getting started with Traefik on Docker
In this tutorial, we will deploy Traefik cloud-native application proxy on Docker.
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 of steps
- Background: What is Traefik
- Deploying Traefik on Docker
- Basic reverse proxy with Traefik
- Loadbalancing HTTP requests across multiple replicas with Traefik
- Routing HTTP requests to multiple backend services with Traefik
- 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.
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:
- Static configurations
- Dynamic configurations
Static configurations - as you may have already guessed - are fairly static. There are two types of static configurations:
- Entrypoints: An entrypoint is the IP + port that Traefik listen for incoming HTTP requests.
- 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:
- Configuration file
- CLI
- 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.
- 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.
- Services: Services are web applications or APIs that serve HTTP requests. The Services configuration defines the URL+port for how to reach a service.
- Middlewares: Middlewares tweak HTTP requests and responses between Entrypoints and Services.
- 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)
- 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. - Login to the virtual server
$ multipass shell docker-host
- 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.
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.
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:
- A web app must be up and running on our Docker Host.
- 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>
.
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.
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.
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.
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.