Before diving in, let’s first establish my use case, which is likely very similar to yours.
I have Docker deployed to my virtualization server with plenty of containers running on it – Portainer, Frigate NVR, Bitwarden, etc. – plus a couple VMs. And I started using Traefik after discovering it with my current employer’s Kubernetes clusters.
For the uninitiated, Traefik Proxy, specifically, allows you to use hostnames to route traffic to your containers rather than relying on port numbers. Then you just need to make sure you have proper DNS resolution for the new hostname. Traefik then uses routing rules to determine which container gets the traffic, keying off the hostname provided via SNI or the Host header in an HTTP request.
And it’s a far less complicated means of putting your browser-accessible containers and services behind an HTTPS proxy.
Almost all of the containers deployed to my Docker server are isolated to their own networks and spun up or down using docker compose. Only two of those Docker services are not behind Traefik: Plex and a MySQL container I use for GnuCash. The MySQL client supports SNI as of version 8.1.0. GnuCash relies on DBD::MySQL, which does not support SNI as of this writing. Plex requires host networking when deployed as a Docker container.
And so does Traefik if you want it to route traffic seamlessly.
Traefik also makes a binary distribution you can run as a service outside Docker. And it might be available from your distro’s package manager. But beware that it may not be the latest version, so you might be missing out on critical security updates like the CVE fix in v3.6.7, the latest version as of this writing.
It’s safe to say, though, that the vast majority of Traefik deployments are via docker compose or the Helm chart for Kubernetes clusters. And as mentioned, I have Traefik deployed via Docker, which is where this comes in:
services:
traefik:
image: traefik:v3.6
container_name: traefik
restart: unless-stopped
network_mode: host
volumes:
- type: bind
source: /var/run/docker.sock
target: /var/run/docker.sock
read_only: true
- type: bind
source: ./certs
target: /certs
read_only: true
- type: bind
source: ./dynamic
target: /dynamic
read_only: true
- type: bind
source: ./traefik.yml
target: /etc/traefik/traefik.yml
labels:
- traefik.enable=true
- traefik.docker.network=traefik
# When using host networking, you can't use the "api@internal" service.
# to access the dashboard. Instead you need to You have to link the
# dashboard (port 8080) to a new one.
- traefik.http.services.traefik.loadbalancer.server.port=8080
- traefik.http.routers.traefik.service=traefik
- traefik.http.routers.traefik.entrypoints=websecure
- traefik.http.routers.traefik.rule=Host(`traefik`) || Host(`traefik.localdomain`)
And alongside the docker-compose.yaml is this traefik.yaml for configuration.
global:
checkNewVersion: true
sendAnonymousUsage: false
log:
level: INFO
accessLog: true
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
permanent: true
websecure:
address: ":443"
http:
tls: true
# Add any other ports you need - e.g., 3306 for MySQL, 6379 for Redis, etc.
providers:
docker:
exposedbydefault: false
file:
filename: /dynamic/tls.yaml
api:
dashboard: true
insecure: true
And this is the folder structure where these files live:
traefik/
|- certs/
|- [container1.crt]
|- [container1.key]
|- [container2.crt]
|- [container2.key]
|- ...
|- dynamic/
|- tls.yaml
|- docker-compose.yml
|- traefik.yml
So the above should be enough to get you started using Traefik with Docker. Change settings where you need – e.g., dumping logs to files instead of the output available via docker logs.
You will need to read the documentation for deploying your individual services with the proper labels so they are properly picked up by Traefik.