Traefik - Dynamic Files
The Dynamic Files for Traefik allow you to configure Routers, Services, Middlewares & Certificate Options all while Traefik is running and without the need for any restarts.
It compliments your existing static configuration.

I use Dynamic files mainly to route services that run on different hosts (different to the host where traefik is installed) through traefik and assign valid SSL certificates.
I also have a dynamic "config.yml" file where I can specify middlewares that can then be called/referenced in other dynamic files which are individually created per service/application.
Info
You can create just one dynamic file but I prefer to keep them separate for ease of maintenance & manageability
config.yml
http:
middlewares:
global-default-headers:
headers:
sslProxyHeaders:
X-Forwarded-Proto: "https"
hostsProxyHeaders:
- "X-Forwarded-Host"
referrerPolicy: "same-origin"
contentTypeNosniff: true
forceSTSHeader: true
stsIncludeSubdomains: true
stsPreload: true
stsSeconds: 31536000
permissionsPolicy: "geolocation=(self), camera=(), microphone=()"
customRequestHeaders:
X-Forwarded-Proto: "https"
customResponseHeaders:
X-Robots-Tag: "none,noarchive,nosnippet,notranslate,noimageindex"
X-XSS-Protection: "0"
server: ""
pihole1-redirect:
redirectRegex:
permanent: true
regex: "^https://ncc-1702.xanderman.co.uk/?$"
replacement: "https://ncc-1702.xanderman.co.uk/admin"
pihole2-redirect:
redirectRegex:
permanent: true
regex: "^https://ncc-1703.xanderman.co.uk/?$"
replacement: "https://ncc-1703.xanderman.co.uk/admin"
pihole3-redirect:
redirectRegex:
permanent: true
regex: "^https://ncc-1704.xanderman.co.uk/?$"
replacement: "https://ncc-1704.xanderman.co.uk/admin"
plex-headers:
headers:
customRequestHeaders:
X-Forwarded-Proto: "https"
sslProxyHeaders:
X-Forwarded-Proto: "https"
xms-csp-headers:
headers:
frameDeny: true
contentSecurityPolicy: >-
default-src 'self';
script-src 'self' 'unsafe-inline';
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
img-src 'self' data: https://img.buymeacoffee.com;
connect-src 'self';
frame-src https://infrastructure.xmsystems.co.uk;
frame-ancestors 'none';
base-uri 'self';
form-action 'self' https://www.buymeacoffee.com;
upgrade-insecure-requests;
xms-infrastructure-csp-headers:
headers:
customResponseHeaders:
X-Frame-Options: ""
contentSecurityPolicy: >-
default-src 'self';
script-src 'self' 'unsafe-inline';
style-src 'self' 'unsafe-inline';
img-src 'self' data:;
font-src 'self';
connect-src 'self';
frame-ancestors https://docs.xmsystems.co.uk;
base-uri 'self';
form-action 'none';
upgrade-insecure-requests;
xms-poker-csp-headers:
headers:
frameDeny: true
contentSecurityPolicy: >-
default-src 'self';
script-src 'self' 'unsafe-inline';
style-src 'self' 'unsafe-inline';
img-src 'self' data:;
font-src 'self';
connect-src 'self' wss://poker.xmsystems.co.uk ws://172.20.0.12:3003;
media-src 'self';
frame-ancestors 'none';
base-uri 'self';
form-action 'none';
upgrade-insecure-requests;
xms-ipam-csp-headers:
headers:
contentSecurityPolicy: >-
default-src 'self';
script-src 'self' 'unsafe-inline';
style-src 'self' 'unsafe-inline';
img-src 'self' data:;
font-src 'self';
connect-src 'self';
frame-ancestors 'none';
base-uri 'self';
form-action 'none';
upgrade-insecure-requests;
xms-blog-csp-headers:
headers:
frameDeny: true
contentSecurityPolicy: >-
default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https://images.unsplash.com;
font-src 'self';
connect-src 'self';
frame-src 'self';
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
upgrade-insecure-requests;
stan-csp-headers:
headers:
frameDeny: true
contentSecurityPolicy: >-
default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval';
style-src 'self' 'unsafe-inline';
img-src 'self' data:;
font-src 'self';
connect-src 'self';
frame-src 'self';
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
upgrade-insecure-requests;
calendar-redirect:
redirectScheme:
scheme: https
permanent: true
Info
This "global-default-headers" middleware is applied directly at each entrypoint within the traefik.yml file. Therefore, they are applied immediately to any and all routes & services and as a result, they are not required to be referenced in any of the below dynamic files for each of my applications
TLS options are also specified in its own dynamic file.
tls.yml
tls:
options:
default:
minVersion: VersionTLS12
cipherSuites:
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
curvePreferences:
- CurveP521
- CurveP384
sniStrict: true
Application Specific Dynamic Files
Primary Pi-Hole (NCC-1702)
http:
routers:
pihole1:
entryPoints:
- "websecure-int"
rule: "Host(`subdomain.domain.co.uk`)"
middlewares:
- pihole1-redirect
tls:
certResolver: production
service: pihole1
services:
pihole1:
loadBalancer:
servers:
- url: "http://10.36.100.2:80"
passHostHeader: true
Alternate Pi-Hole (NCC-1703)
http:
routers:
pihole2:
entryPoints:
- "websecure-int"
rule: "Host(`subdomain.domain.co.uk`)"
middlewares:
- pihole2-redirect
tls:
certResolver: production
service: pihole2
services:
pihole2:
loadBalancer:
servers:
- url: "http://10.36.100.3:80"
passHostHeader: true
2nd Alternate Pi-Hole (NCC-1704)
http:
routers:
pihole3:
entryPoints:
- "websecure-int"
rule: "Host(`subdomain.domain.co.uk`)"
middlewares:
- pihole3-redirect
tls:
certResolver: production
service: pihole3
services:
pihole3:
loadBalancer:
servers:
- url: "http://10.36.100.151:80"
passHostHeader: true
The Pi-Hole's dynamic config file has a "redirectRegex" middleware to replace the URL specified with one that adds /admin onto the end of the URL which the Pi-Hole web interface requires. This middleware is referenced in each of the pi-hole's dynamic files and the middleware config itself is outlined within the main config dynamic file along with the headers.
Also, to allow traefik to handle the secure connection and not have pi-hole re-direct from 80 to 443, I have amended the following lines in the pihole.toml file located in /etc/pihole on each Pi:
Dozzle (Titan)
http:
routers:
dozzle:
entryPoints:
- websecure-int
rule: "Host(`subdomain.domain.co.uk`)"
tls:
certResolver: production
service: dozzle
services:
dozzle:
loadBalancer:
servers:
- url: "http://10.36.100.150:8585"
passHostHeader: true
Prometheus (Tethys)
http:
routers:
prom:
entryPoints:
- websecure-int
rule: "Host(`subdomain.domain.co.uk`)"
tls:
certResolver: production
service: prom
services:
prom:
loadBalancer:
servers:
- url: "http://10.36.100.152:9090"
passHostHeader: true
Grafana (Tethys)
http:
routers:
graphs:
entryPoints:
- websecure-int
rule: "Host(`subdomain.domain.co.uk`)"
tls:
certResolver: production
service: graphs
services:
graphs:
loadBalancer:
servers:
- url: "http://10.36.100.152:3000"
passHostHeader: true
phpMyAdmin (Titan)
http:
routers:
phpmyadmin-titan:
entryPoints:
- websecure-int
rule: "Host(`subdomain.domain.co.uk`)"
tls:
certResolver: production
service: phpmyadmin-titan
services:
phpmyadmin-titan:
loadBalancer:
servers:
- url: "http://172.19.0.105:80"
passHostHeader: true
phpMyAdmin (Phobos)
http:
routers:
phpmyadmin-phobos:
entryPoints:
- websecure-int
rule: "Host(`subdomain.domain.co.uk`)"
tls:
certResolver: production
service: phpmyadmin-phobos
services:
phpmyadmin-phobos:
loadBalancer:
servers:
- url: "http://10.36.100.151:81"
passHostHeader: true
phpMyAdmin (Tethys)
http:
routers:
phpmyadmin-tethys:
entryPoints:
- websecure-int
rule: "Host(`subdomain.domain.co.uk`)"
tls:
certResolver: production
service: phpmyadmin-tethys
services:
phpmyadmin-tethys:
loadBalancer:
servers:
- url: "http://10.36.100.152:81"
passHostHeader: true
MotionEye (Phobos)
http:
routers:
cctv:
entryPoints:
- "websecure-int"
rule: "Host(`subdomain.domain.co.uk`)"
tls:
certResolver: production
service: cctv
services:
cctv:
loadBalancer:
servers:
- url: "http://10.36.100.151:8765"
passHostHeader: true
Uptime-Kuma (Phobos)
http:
routers:
kuma:
entryPoints:
- "websecure-int"
rule: "Host(`subdomain.domain.co.uk`)"
tls:
certResolver: production
service: kuma
services:
kuma:
loadBalancer:
servers:
- url: "http://10.36.100.151:3001"
passHostHeader: true
Portainer (Phobos)
http:
routers:
portainer-phobos:
entryPoints:
- "websecure-int"
rule: "Host(`subdomain.domain.co.uk`)"
tls:
certResolver: production
service: portainer-phobos
services:
portainer-phobos:
loadBalancer:
servers:
- url: "https://10.36.100.151:9443"
passHostHeader: true
PH-Intercept (Phobos)
http:
routers:
ph-intercept:
entryPoints:
- websecure-int
rule: "Host(`subdomain.domain.co.uk`)"
tls:
certResolver: production
service: ph-intercept
services:
ph-intercept:
loadBalancer:
servers:
- url: "http://10.36.100.151:4653"
passHostHeader: true
Unifi (UCG)
http:
routers:
unifi:
entryPoints:
- "websecure-int"
rule: "Host(`subdomain.domain.co.uk`)"
tls:
certResolver: production
service: unifi
services:
unifi:
loadBalancer:
servers:
- url: "https://10.36.100.1:443"
passHostHeader: true
Note
Unifi's Web UI listens on 443 so the URL needs to be HTTPS
CheckMK (Tethys)
http:
routers:
cmk:
entryPoints:
- websecure-int
rule: "Host(`subdomain.domain.co.uk`)"
tls:
certResolver: production
service: cmk
services:
cmk:
loadBalancer:
servers:
- url: "http://10.36.100.152:80"
passHostHeader: true
Portainer (Tethys)
http:
routers:
portainer-tethys:
entryPoints:
- "websecure-int"
rule: "Host(`subdomain.domain.co.uk`)"
tls:
certResolver: production
service: portainer-tethys
services:
portainer-tethys:
loadBalancer:
servers:
- url: "https://10.36.100.152:9443"
passHostHeader: true
XMS-Blog (Titan)
http:
routers:
blog-xms:
entryPoints:
- websecure-ext
rule: "Host(`blog.xmsystems.co.uk`)"
middlewares:
xms-blog-csp-headers
tls:
certResolver: production
service: blog-xms
services:
blog-xms:
loadBalancer:
servers:
- url: "http://ghost-xms:2368"
passHostHeader: true