a blog post series to my homelab
check here for Introduction to Traefik - Part One
Part Two
In this blog post series, I will connect several docker containers and a virtual machine behind the Traefik reverse proxy on my homelab, and set up Let’s Encrypt for TLS. In this post, I will connect our first docker container to the Traefik reverse proxy for testing and to learn how to do this.
I’ve also made a short video to accompany this blog post:
WhoAmI?
Traefik, whoami is often used as a simple test service to demonstrate how Traefik handles routing, especially when using dynamic routing and reverse proxy setups.
# A container that exposes an API to show its IP address
whoami:
image: traefik/whoami
container_name: whoami
hostname: whoami
our updated docker compose file should look like:
docker-compose.yml
---
services:
traefik:
image: traefik:v3.3
container_name: traefik
hostname: traefik
env_file:
- path: ./.env
required: true
restart: unless-stopped
ports:
# The Web UI (enabled by --api.insecure=true)
- 8080:8080
# The HTTP port
- 80:80
volumes:
- ./traefik:/etc/traefik
- /var/run/docker.sock:/var/run/docker.sock:ro
# A container that exposes an API to show its IP address
whoami:
image: traefik/whoami
container_name: whoami
hostname: whoami
Start all the services
docker compose up -d
output:
$ docker compose up -d
[+] Running 3/3
✔ Network homelab_default Created 0.3s
✔ Container whoami Started 2.2s
✔ Container traefik Started 2.3s
Test WhoAmI - the wrong way
We can test our traefik reverse proxy with
curl -H Host:whoami.localhost http://127.0.0.1
but the result should be:
404 page not found
Why ?
We have not yet changed our traefik configuration file to enable an EntryPoint. EntryPoints are the network entry points into Traefik.
So let’s go back one step.
docker compose down
Traefik Configuration
The simplest approach is to update our configuration file and add a new EntryPoint. However, I’d like to take this opportunity to expand our configuration a little.
EntryPoints are Static Configuration, so they must be included on traefik.yml file.
## Static configuration
entryPoints:
web:
address: ":80"
so traefik/traefik.yml should look like:
# API and dashboard configuration
api:
insecure: true
## Static configuration
entryPoints:
web:
address: ":80"
That should work, but given the opportunity, let’s enhance our Traefik configuration by including:
Ping
# The /ping health-check URL
ping: {
}
It would be useful to add a health check in the Docker Compose file later on.
Logs
This is how to enable the DEBUG (or INFO - just replace the verb in level)
# Debug log
log:
filePath: /etc/traefik/traefik.log
level: DEBUG
Docker provider
I want to explicitly include the Docker provider with a caveat: I don’t want to automatically expose all my docker containers behind Traefik. Instead, I prefer to manually add each docker container that I want to expose to the internet, rather than exposing all of them by default.
providers:
# Enable docker provider
docker: {
exposedByDefault: false
}
Traefik Configuration file updated
and now traefik/traefik.yml looks like:
# The /ping health-check URL
ping: {
}
# API and dashboard configuration
api:
insecure: true
# Debug log
log:
filePath: /etc/traefik/traefik.log
level: DEBUG
# Enable EntryPoints
entryPoints:
web:
address: ":80"
reusePort: true
# Providers
providers:
# Enable docker provider
docker: {
exposedByDefault: false
}
by running
docker compose up traefik -d
we can review Traefik dashboard with the new web EntryPoint and ping
WhoAmI - the correct way
okay, we now have our EntryPoint in Traefik but we need to explicit expose our whoami docker container and in order to do that, we need to add some labels!
# A container that exposes an API to show its IP address
whoami:
image: traefik/whoami
container_name: whoami
hostname: whoami
labels:
- "traefik.enable=true" # To enable whoami to Traefik
- "traefik.http.routers.whoami.rule=Host(`whoami.localhost`)" # Declare the host rule for this service
- "traefik.http.routers.whoami.entrypoints=web" # Declare the EntryPoint
let’s put everything together:
---
services:
traefik:
image: traefik:v3.3
container_name: traefik
hostname: traefik
env_file:
- path: ./.env
required: true
restart: unless-stopped
ports:
# The Web UI (enabled by --api.insecure=true)
- 8080:8080
# The HTTP port
- 80:80
volumes:
- ./traefik:/etc/traefik
- /var/run/docker.sock:/var/run/docker.sock:ro
# A container that exposes an API to show its IP address
whoami:
image: traefik/whoami
container_name: whoami
hostname: whoami
labels:
- "traefik.enable=true" # To enable whoami to Traefik
- "traefik.http.routers.whoami.rule=Host(`whoami.localhost`)" # Declare the host rule for this service
- "traefik.http.routers.whoami.entrypoints=web" # Declare the EntryPoint
docker compose up -d
Test Who Am I
curl -H Host:whoami.localhost http://127.0.0.1
output:
Hostname: whoami
IP: 127.0.0.1
IP: ::1
IP: 172.19.0.3
RemoteAddr: 172.19.0.2:41276
GET / HTTP/1.1
Host: whoami.localhost
User-Agent: curl/8.12.1
Accept: */*
Accept-Encoding: gzip
X-Forwarded-For: 172.19.0.1
X-Forwarded-Host: whoami.localhost
X-Forwarded-Port: 80
X-Forwarded-Proto: http
X-Forwarded-Server: traefik
X-Real-Ip: 172.19.0.1
Health Checks and Depends
before finishing this article, I would like to include two more things.
Traefik Health Check
We added above the ping section on Traefik configuration, it is time to use it. On our docker compose configuration file, we can add a health check section for Traefik service.
We can test this from our command line
curl -s --fail http://127.0.0.1:8080/ping
the result should be an OK !
and we can extend the Traefik service to include this
healthcheck:
test: curl -s --fail http://127.0.0.1:8080/ping
interval: 30s
retries: 3
timeout: 10s
start_period: 10s
Depends On
The above health check option can be used to specify service dependencies in docker compose, so we can ensure that the whoami docker service starts after Traefik.
depends_on:
- traefik
that means our docker compose yaml file should look like:
---
services:
traefik:
image: traefik:v3.3
container_name: traefik
hostname: traefik
env_file:
- path: ./.env
required: true
restart: unless-stopped
ports:
# The Web UI (enabled by --api.insecure=true)
- 8080:8080
# The HTTP port
- 80:80
volumes:
- ./traefik:/etc/traefik
- /var/run/docker.sock:/var/run/docker.sock:ro
# Add health check
healthcheck:
test: curl -s --fail http://127.0.0.1:8080/ping
interval: 30s
retries: 3
timeout: 10s
start_period: 10s
# A container that exposes an API to show its IP address
whoami:
image: traefik/whoami
container_name: whoami
hostname: whoami
depends_on:
- traefik
labels:
- "traefik.enable=true" # To enable whoami to Traefik
- "traefik.http.routers.whoami.rule=Host(`whoami.localhost`)" # Declare the host rule for this service
- "traefik.http.routers.whoami.entrypoints=web" # Declare the EntryPoint
a blog post series to my homelab
Part One
In this blog post, I will start by walking you through the process of setting up Traefik as a reverse proxy for your homelab. My setup involves using a virtual machine connected via a point-to-point VPN to a cloud VPS, along with several Docker containers on my homelab for various tasks and learning purposes. The goal is to expose only Traefik to the internet, which will then provide access to my internal homelab. For certain applications, I also use Tailscale, which I prefer not to expose directly to the internet. In short, I have a complex internal homelab setup, and in this post, we’ll simplify it!
I’ve made a short video to accompany this blog post:
docker compose
To begin, we need to set up a basic Docker Compose YAML file.
As of the time of writing this blog post, the latest Traefik Docker container image is version 3.3. It is best to declare a specific version instead of using “latest” tag.
image: traefik:v3.3
Using an .env file in a Docker Compose configuration is important for several reasons, as for configure variables, secrets and it is easy to reuse though several services and to avoid hardcoding values. For traefik is important so we can configure the docker GID in order traefil to be able to use the docker socket.
eg. .env
# This is my user id
PUID=1001
# This is my docker group id
PGID=142
UMASK="002"
TZ="Europe/Athens"
DNS="88.198.92.222"
Next interesting topic is the volumes section.
I would like to mount a local directory for the traefik configuration, which I will later use with the dynamic file provider. Additionally, to enable Traefik to recongize our (future) docker images, we need to mount the docker socket too.
volumes:
- ./traefik:/etc/traefik
- /var/run/docker.sock:/var/run/docker.sock
to conclude, here is a very basic docker compose yaml file:
docker-compose.yml
---
services:
traefik:
image: traefik:v3.3
container_name: traefik
hostname: traefik
env_file:
- path: ./.env
required: true
restart: unless-stopped
ports:
# The Web UI (enabled by --api.insecure=true)
- 8080:8080
# The HTTP port
- 80:80
volumes:
- ./traefik:/etc/traefik
- /var/run/docker.sock:/var/run/docker.sock
pull traefik docker image
we can explicitly get the Traefik docker container image
docker compose pull traefik
traefik configuration file
we also need to create the configuration file by enabling the API and the dashboard for now.
Create the directory and file
traefik/traefik.yml
and write this:
# API and dashboard configuration
api:
insecure: true
Start traefik docker
We are ready start and run our Traefik docker container:
docker compose up
result is something like:
[+] Running 2/2
✔ Network homelab_default Created 0.3s
✔ Container traefik Created 0.4s
Attaching to traefik
To stop traefik from docker compose, we need to open a new terminal and type from the same directory
docker compose down
or, we ca run the docker compose and detach it so it runs on the background:
docker compose up traefik -d
This is going to be useful for the future as we starting each service one by one.
Test traefik
Open your browser and click on: http://127.0.0.1:8080
you will see something like: