Table of Contents
Basic Configuration
This is a copy-paste ready collection of recipes, aimed to run dockerized Traefik proxy and proxying other services. This recipes tailored to run in Docker's standalone mode. You can mix and match them at your digression, but be aware that running same recipes in Docker Swarm may require some adaptation.
Traefik can accept configuration in various ways. I will use Docker labels for this purpose.
Traefik: The Proxy Itself
Here is a basic Traefik service, configured using command line parameters and Docker service labels:
services:
traefik:
image: traefik:3.4
restart: unless-stopped
command:
# if you need more details in logs
# change `INFO` to `DEBUG`
- --log.level=INFO
- --accesslog=true
# provider options
# for swarm swap coment in next two lines
- --providers.docker=true
# - --providers.docker.swarmMode=true
- --providers.docker.exposedbydefault=false
# entrypoints
- --entrypoints.web.address=:80
ports:
- "80:80"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- letsencrypt:/letsencrypt
labels:
traefik.enable: false
networks:
- internal
networks:
internal:
volumes:
letsencrypt:
Whoami: Our Test Subject
This service will become our guinea pig for all following experiments. I will use traefik/whoami
image, but really, you can use anything you want. I choose it, because it can display some information about requests, which will be handy.
A basic service definition is not complicated:
whoami:
image: "traefik/whoami"
labels:
# mark a container for Traefik to notice it
traefik.enable: true
# let Traefil know that we want this domain name for a container
traefik.http.routers.whoami.rule: Host(`whoami.localhost`)
# let Traefik know to expect a container answer that port
traefik.http.services.whoami.loadbalancer.server.port: 80
networks:
- internal
Let's check how everything works. Deploy the stack and open http://whoami.localhost in your browser.

Basic configuration working as intended
The Headers
Here's our request headers, shown by whoami
service. Those are pretty common headers, with some exceptions.
Hostname: 82add48227da
IP: 127.0.0.1
IP: ::1
IP: 172.20.0.2
RemoteAddr: 172.20.0.3:36092
GET / HTTP/1.1
Host: whoami.localhost
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:129.0) Gecko/20100101 Firefox/129.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: en-US,en;q=0.5
Priority: u=0, i
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: cross-site
Upgrade-Insecure-Requests: 1
X-Forwarded-For: 172.20.0.1
X-Forwarded-Host: whoami.localhost
X-Forwarded-Port: 80
X-Forwarded-Proto: http
X-Forwarded-Server: 9f5eb482116f
X-Real-Ip: 172.20.0.1
Take a look at highlighted lines. Those headers are so-called Forwarding headers. They are not part of original request and added by Traefik itself and are essential to proper proxy-to-app interaction. Laravel application, for example, can detect SSL is available and generate correct links based on X-Forwarded-Proto
header.
HTTPS
This addition requred with any chalenge
traefik:
#...
command:
#...
# ssl
- --entrypoints.websecure.http3
- --entrypoints.websecure.address=:443
- --entrypoints.web.http.redirections.entryPoint.to=websecure
- --entrypoints.web.http.redirections.entryPoint.scheme=https
- --entrypoints.web.http.redirections.entrypoint.permanent=true
# ...
ports:
#...
- "443:443/tcp"
- "443:443/udp"
#...
#...
For Traefik, we're adding a new entrypoint called websecure, assigning port 443 to it and establishing permanent redirects to it. Scheme for redirects will be changed to https. Also, for Docker, we need to mention protocols now. By default, Docker will open port for TCP traffic only. HTTP3 uses UDP as a transport protocol, hence same port with another protocol.
Self-Signed certificate
Configuration above should be sufficient to provide us with self-signed certificate, generated by Traefik. We need to add a couple of lines to service container though:
whoami:
labels:
#...
# SSL
traefik.http.routers.whoami.tls: true
#...
Deploy the stack and try to access http://whoami.localhost or https://whoami.localhost

Firefox complaining about self-signed certificate

Advanced... button will reveal additional details

Self-Signed certificate datails

Whoami page, served via HTTPS
After modifications done, re-deploy the stack and check, if it still works.
In our case - the local deployment, Traefik will issue a self-signed certificate and browser (Firefox in my case) will not be happy about it. Click Accept risk and continue to let it know that it's ok. The browser will open whoami
page as usual. The only reminder will be a crossed out https badge in address bar.
Note, that X-Forwarded-Port
is now 443 and X-Forwarded-Proto
- https.
Now, for Real
Lets setup TLS for our services. As a bonus we will get HTTP2 and HTTP3 running with almost no additional configuration.
traefik:
#...
command:
#...
# ssl
- --entrypoints.websecure.http3
- --entrypoints.websecure.address=:443
- --entrypoints.web.http.redirections.entryPoint.to=websecure
- --entrypoints.web.http.redirections.entryPoint.scheme=https
- --entrypoints.web.http.redirections.entrypoint.permanent=true
# ssl cert provider
- --certificatesresolvers.myresolver.acme.tlschallenge=true
# - --certificatesresolvers.myresolver.acme.httpchallenge=true
# - --certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web
- --certificatesresolvers.myresolver.acme.email=${LE_EMAIL:?LE_EMAIL not set}
- --certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json
# - --certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/director
#...
ports:
#...
- "443:443/tcp"
- "443:443/udp"
#...
#...
Lines from 12 to 17 are options related to ACME protocol which used to automatically issue and renew TLS certificates. In order to get certificate issued, you need to prove your domain ownership. There are a couple strategies available for this:
Option called email
defines an email which, will be used to create an ACME account. The storage
option defines where to store issued certificate. Ideally storage
should point to persistent storage of some sort. A Docker volume, for example.
Additionally, you can use caserver
option to define your ACME server of choice. In case of Let's Encrypt you may want to set it to https://acme-staging-v02.api.letsencrypt.org/director
, while experimenting. It will instruct Traefik to use LE's staging servers, and save you from getting temporary banned for frequent requests. I believe you can plug your own CA authority here (something like this, this or this) and get it working too. Main point - ACME protocol support. Not tested, though.
Now we need to instruct Traefik to use TLS for particular service:
whoami:
labels:
#...
# SSL
traefik.http.routers.whoami.tls: true
traefik.http.routers.whoami.entrypoints: websecure
#...
We're instructing Traefik to use TLS for our service and use entry point called websecre
for traffic. Latter may be unnecessary, but better to have that line there anyway.
The End?
I tried my best to fit all my recipes/snippets in one post. They just keep piling up, as I scanned through my notes. To that extent, I managed to fit just a basic setup here. And I want to post something already! So, I will call it the end of part one. Stay tuned for part two!