···8181 * Has first-class docker service discovery via labels and supports [multiple hosts](https://github.com/yusing/godoxy/wiki/Configurations#setting-up-providers)
8282 * Strongly considered but too new...lack of docs and user base. My homelab is too large and in "production use" to be a beta tester
83838484-## Satifying Requirements with Traefik
8484+## Satisfying Requirements with Traefik
85858686I eventually settled on [Traefik](https://traefik.io/) after due-diligence gave me enough confidence to think I could satisfy all of the [Requirement](#requirementsspec).
8787···93939494This was the hardest requirement to meet for **edge cases**. Unlike NGINX,
95959696-* Chaining together multiple transformations of a route is not as straightforward
9796* Doing anything that short-circuits a route and isn't already a middleware isn't well documented
9897* Traefik doesn't have as many middlewares (plugins) and well-documented, non-trivial "how to do X" examples
9998* Traefik's docs mostly use file provider examples which have 1-to-1 equivalents in [docker provider labels](https://doc.traefik.io/traefik/providers/docker/#configuration-examples) but labels examples are basically non-existent. Tracking down examples of *complex* labels usage was frustrating.
···267266268267#### How do I do X?
269268270270-Check the [FAQ](#faq-and-how-tos) at the bottom for more examples like
269269+Check the [FAQ](#faq) at the bottom for more examples like
271270272271* [Chaining middleware](#chaining-middleware-with-non-trivial-examples)
273272* [Redirect on non-existent Route](#redirect-on-non-existent-route)
···13771376```
13781377{: file="static_config/traefik.yaml" }
1379137813801380-## Traefik Gotchas
13791379+## FAQ, Gotchas, and How To's {#faq}
1381138013821382-### Ambiguous Config Sources and Documentation
13811381+### Static/Dynamic Config Explained
1383138213841384-* Difficult to easily grok what the difference between static/dynamic config is
13851385-* Difficult to determine where static/dynamic config should be placed in dir/files
13861386-* Not obvious that config from separate sources can be used anywhere (file middleware in labels, docker plugin config in files)
13871387- * Parsed config sources not listed anywhere by name
13831383+The Traefik docs do not do a good job of explaining the differences between these, where they can be placed, and what belongs in each.
13841384+13851385+#### Static Config
13861386+13871387+[Static Config](https://doc.traefik.io/traefik/getting-started/configuration-overview/#the-static-configuration) is anything that affects Traefik's behavior as a whole (logging, auth, plugins) or is considered "root" behavior like entrypoints, certificates, general provider configuration. Changes to Static Configuration does not take affect until Traefik is restarted.
13881388+13891389+Within each provider of [Configuration Discovery](https://doc.traefik.io/traefik/providers/overview/), everything found under the [Provider Configuration](https://doc.traefik.io/traefik/providers/docker/#provider-configuration) section is **Static Configuration.** Everything else related to that Provider (routing, service, loadbalancers, etc...) is **Dynamic Configuration.**
13901390+13911391+Static Configuration can be set in three places:
13921392+13931393+##### File {#static-file-reference}
13941394+13951395+In a [**specific configuration file**](https://doc.traefik.io/traefik/getting-started/configuration-overview/#configuration-file):
13961396+13971397+* `/etc/traefik/traefik.yaml`
13981398+* `$HOME/.config/traefik.yaml`
13991399+* `./traefik.yaml` (the working directory)
14001400+* a file defined as an argument to the traefik program IE `traefik --configFile=foo/bar/myconfigfile.yml`
14011401+14021402+**Dynamic and Static Configuration files cannot be mixed.**
14031403+14041404+[**File Reference**](https://doc.traefik.io/traefik/reference/static-configuration/file/)
14051405+14061406+##### CLI {#cli-cli-reference}
14071407+14081408+As a CLI argument to the traefik program. This can be plain command line or more commonly as part of `compose.yaml`. Example of same args in both places:
14091409+14101410+```shell
14111411+traefik --providers.docker=true --providers.docker.exposedbydefault=false --providers.file.directory=/etc/traefik/dynamic
14121412+```
14131413+14141414+```yaml
14151415+ traefik:
14161416+ image: "traefik:v3.3"
14171417+ command:
14181418+ - "--providers.docker=true"
14191419+ - "--providers.docker.exposedbydefault=false"
14201420+ - "--providers.file.directory=/etc/traefik/dynamic"
14211421+ # ...
14221422+```
14231423+14241424+The CLI args used for compose `command` **cannot be used as labels for the [Docker Provider.](https://doc.traefik.io/traefik/providers/docker/) They are not equivalent.**
14251425+14261426+[**CLI Reference**](https://doc.traefik.io/traefik/reference/static-configuration/cli/)
14271427+14281428+##### ENV {#static-env-reference}
14291429+14301430+As environmental variables passed to the program or in `environment:` in `compose.yaml`. Again, ENVs for Static Configuration **cannot be used as labels for the [Docker Provider.](https://doc.traefik.io/traefik/providers/docker/) They are not equivalent.**
14311431+14321432+[**ENV Reference**](https://doc.traefik.io/traefik/reference/static-configuration/env/)
14331433+14341434+#### Dynamic Configuration
14351435+14361436+[Dynamic Config](https://doc.traefik.io/traefik/getting-started/configuration-overview/#the-dynamic-configuration) is everything related to wiring up [Routers](https://doc.traefik.io/traefik/routing/overview/), [Services](https://doc.traefik.io/traefik/routing/services/),[Middlewares](https://doc.traefik.io/traefik/routing/services/), etc...
14371437+14381438+**All** Dynamic config is parsed from one or more [Providers](https://doc.traefik.io/traefik/providers/overview/) that is setup using a [Static Config](#static-config).
14391439+14401440+Importantly, regular YAML/TOML files can be parsed as "dynamic" config using the [File provider](https://doc.traefik.io/traefik/providers/file/). To use files/directories as dynamic config they need to defined in the Static Config first like:
14411441+14421442+```yaml
14431443+providers:
14441444+ file:
14451445+ directory: "/config"
14461446+```
14471447+{: file="/etc/traefik/traefik.yaml"}
14481448+14491449+**Dynamic Config cannot be parsed from Static Config.** All of the methods/locations shown in the [Static Config](#static-config) cannot be used for Dynamic Config.
14501450+14511451+### Static/Dynamic Config Best Practices
14521452+14531453+This is entirely opinionated but as I have implemented Traefik for 60+ stacks across 7 machines with 4 different entrypoints, 5 plugins, and multiple certificates I have pretty good handle on what is manageable and scalable when it comes to how to best place config.
14541454+14551455+Whatever you end up doing, it's best to keep similar features together:
14561456+14571457+* Don't define 3 plugins in static file and 2 in CLI
14581458+* Do define one-use middleware in the same docker labels as the serivce using it
14591459+* Don't define a middleware in Servicea in `composeA.yaml` on HostA and then use it in ServiceY in `composeZ.yaml` on HostB. Move it into a central location instead.
14601460+14611461+#### Static Config In Files {#static-file}
14621462+14631463+Static config tends to have the most lists and nested properties. This becomes cumberbose to define in CLI as each section needs to be repeated for each nested property. It's also likely to be the largest block of config and benefits from being layed out visually in YAML as easier to read.
14641464+14651465+```yaml
14661466+command:
14671467+ - "--entryPoints.web.address=80"
14681468+ - "--entryPoints.web.http.middlewares=rate-limit@file"
14691469+ - "--entryPoints.web.forwardedHeaders.trustedIPs[0]=172.28.0.1/24"
14701470+ - "--entryPoints.web.forwardedHeaders.trustedIPs[1]=172.20.0.1/24"
14711471+```
14721472+vs
14731473+```yaml
14741474+ web:
14751475+ address: :80
14761476+ http:
14771477+ middlewares:
14781478+ - rate-limit@file
14791479+ forwardedHeaders:
14801480+ trustedIPs:
14811481+ - 172.28.0.1/24
14821482+ - 172.20.0.1/24
14831483+```
14841484+14851485+I keep my static config mounted to `/etc/traefik/traefik.yaml` inside the traefik docker container.
14861486+14871487+```yaml
14881488+ traefik:
14891489+ image: "traefik:v3.3"
14901490+ # ...
14911491+ volumes:
14921492+ - /host/path/to/static_dir:/etc/traefik
14931493+```
14941494+{: file="compose.yaml"}
14951495+14961496+#### Dynamic Config In Labels {#dynamic-file}
14971497+14981498+Any [Dynamic Config](#dynamic-configuration) that will be used by multiple routers/services should written in a YAML file parsed by the [File provider](https://doc.traefik.io/traefik/providers/file/). This prevents you from accidentally changing a middleware that may be used by more than one service or even deleting the middleware entirely. For example, if it was only defined in the docker compose labels and the service was destroyed then it would delete the middleware.
14991499+15001500+Additionally, any config that requires long lists, deeply-nested properties, or defining 10+ properties may also benefit from being in a file for readability. I tend to keep all my reusable middlewares in a file named `global.yaml` with a second/third file for non-docker sites or those not possible to define with labels (`sites.yaml`).
15011501+15021502+The [File provider](https://doc.traefik.io/traefik/providers/file/) is configured to parse dynamic configs from `/config` mounted into the traefik container.
15031503+15041504+```yaml
15051505+providers:
15061506+ file:
15071507+ directory: "/config"
15081508+```
15091509+{: file="/etc/traefik/traefik.yaml"}
15101510+15111511+```yaml
15121512+ traefik:
15131513+ image: "traefik:v3.3"
15141514+ # ...
15151515+ volumes:
15161516+ - /host/path/to/static_dir:/etc/traefik
15171517+ - /host/path/to/dynamic_dir:/config
15181518+```
15191519+{: file="compose.yaml"}
15201520+15211521+#### Reusable Dynamic Config In Files {#dynamic-label}
15221522+15231523+All routers/services/middlewares that are specific to a docker service are defined using [docker labels](https://doc.traefik.io/traefik/providers/docker/#routing-configuration-with-labels) on that service (with exceptions [mentioned above](#dyanmic-file)). The docker service should "own" as much of the configuration for defining how it is wired up to traefik as possible.
15241524+15251525+```yaml
15261526+services:
15271527+ serviceA:
15281528+ # ...
15291529+ labels:
15301530+ traefik.enable: true
15311531+ traefik.http.routers.serviceA.rule: Host(`serviceA.example.com`)
15321532+ traefik.http.services.serviceA.loadbalancer.server.port: 3000
15331533+ traefik.docker.network: internal_overlay
15341534+```
15351535+{: file="compose.yaml"}
1388153613891537### Finding Config Errors
1390153813911391-* Dynamic config issues immediate feedback, visible in dashboard
13921392-* Static config does not appear until restart and traefik will bulldoze over silently
13931393- * Errors only show in docker logs but traefik will startup anyways
13941394- * May cause dashboard to entirely disappear if error affects internal routes
13951395- * Errors may look like dynamic if the error trickles downstream
13961396-* So..always check docker logs **first** and always restart traefik after any static config changes
15391539+Errors in [dynamic configuration](https://doc.traefik.io/traefik/providers/overview/) or downstream services can be found using the [internal dashboard.](https://doc.traefik.io/traefik/operations/dashboard/) The same is **not true** for [static configuration](https://doc.traefik.io/traefik/getting-started/configuration-overview/#the-static-configuration)which:
15401540+15411541+* Does not appear until restart and traefik will bulldoze over silently
15421542+* Errors only show in docker logs but traefik will startup anyways
15431543+* May cause dashboard to entirely disappear if error affects internal routes
15441544+* Errors may look like dynamic if the error trickles downstream
15451545+15461546+So..always check docker logs **first** and always restart traefik after any static config changes. This is also why [access logs](#access-logs) should be separated from docker logs -- so that regular access noise output to docker logs does not drown out errors in traefik that are only output to docker logs.
1397154713981548### Clobbering Labels
1399154914001400-* Copy-pasting labels, may forget service/router name is same as existing
14011401-* Traefik does not complain about this unless it causes actually issues (router issue) but will still cause reachability issues while running
15501550+This is mainly applicable when using [routing configuration with labels (Docker compose labels)](https://doc.traefik.io/traefik/providers/docker/#routing-configuration-with-labels).
1402155114031403-## FAQ and How To's
15521552+Traefik only uses uniquely named configuration for routers/services. It also *does not complain* if you use the same named labels more than once and will **silently overwrite** configuration. Take this example:
1404155314051405-### Chaining Middleware with Non-Trivial Examples
15541554+```yaml
15551555+services:
15561556+ serviceA:
15571557+ image: myFooImage
15581558+ labels:
15591559+ traefik.http.routers.myRouterA.rule: Host(`service.example.com`)
15601560+ traefik.http.services.serviceA.loadbalancer.server.port: 8080
15611561+ serviceB:
15621562+ image: myBarImage
15631563+ labels:
15641564+ traefik.http.routers.myRouterA.rule: Host(`service.example.org`)
15651565+ traefik.http.services.serviceA.loadbalancer.server.port: 8090
15661566+```
1406156714071407-Mastodon example
15681568+Both services reference `myRouterA` and `serviceA` but have different values for rule/port. Whichever service was deployed last will overwrite config for `myRouterA`/`serviceA`, leaving the other one unreachable! Additionally, if the overwritten configuration is still valid within Traefik there will be *no errors!*
15691569+15701570+So,
15711571+15721572+* make sure that when copy-pasting services/labels that the names are changed
15731573+* if a service is not reachable and copy-pasting or referencing existing services was used check for label clobbering first before looking anywhere else
1408157414091575### Redirect on non-existent Route
1410157614111411-Return 302 instead of 404 for wildcare routes.
15771577+This configuration makes Traefik redirect to *any* site of your choice (not just an existing Service) if no other Route is matched.
1412157814131413-### Missing How Do To X Example
15791579+This needs to be done at least partially in a [dynamic config (File provider)]([dynamic config file](https://doc.traefik.io/traefik/providers/file/)).
1414158014151415-* Minio for mastodon, `customresponseheaders`
14161416-* `ignorecert` and `insecureSkipVerify` usage
15811581+Create a new [Middleware](https://doc.traefik.io/traefik/middlewares/overview/) that uses a [RedirectRegex](https://doc.traefik.io/traefik/middlewares/http/redirectregex/) to redirect *anything* to the site of your choice.
15821582+15831583+Then, add a new [Router](https://doc.traefik.io/traefik/routing/routers/) to the dynamic config. Explanation:
15841584+15851585+* `PathPrefix('/')` makes the router match any route
15861586+* `priority: 1` uses a low [priority](https://doc.traefik.io/traefik/routing/routers/#priority) to ensure the rule is run **after** any other Routers.
15871587+* `middlewares: anyreg` makes the route redirect (always)
15881588+* `service: noop@internal` is an undocumented "dummy" service that can be used for redirect [#1](https://github.com/traefik/traefik/issues/7291) [#2](https://community.traefik.io/t/noop-internal-service/5165) [#3](https://github.com/traefik/traefik/issues/7242#issuecomment-692101030)
15891589+15901590+```yaml
15911591+http:
15921592+ middlewares:
15931593+ anyreg:
15941594+ redirectregex:
15951595+ regex: ^.*
15961596+ replacement: https://example.com
15971597+ routers:
15981598+ catchall:
15991599+ rule: "PathPrefix(`/`)"
16001600+ priority: 1
16011601+ middlewares: anyreg
16021602+ service: noop@internal
16031603+ entryPoints:
16041604+ - yourEntryPoint
16051605+```
16061606+{: file="/config/global.yaml"}
16071607+16081608+### Trust Service Self-Generated Certificate
16091609+16101610+If you have a Service/container that self-signs its own SSL certificates and using that path is the only way to access the service -- IE when you visit the URL in browser you get warning about self-signed certificates -- Traefik can be configured to always accept these certs so the warning does not occur or cause issues for Traefik.
16111611+16121612+This needs to be configured in a [dynamic config (File provider)]([dynamic config file](https://doc.traefik.io/traefik/providers/file/)).
16131613+16141614+Create a new [ServersTransport](https://doc.traefik.io/traefik/routing/services/#serverstransport_1) configuration that uses [`insecureSkipVerify`](https://doc.traefik.io/traefik/routing/services/#insecureskipverify):
16151615+16161616+```yaml
16171617+http:
16181618+ # ...
16191619+ serversTransports:
16201620+ ignorecert:
16211621+ insecureSkipVerify: true
16221622+```
16231623+{: file="/config/global.yaml"}
16241624+16251625+Then, on the docker labels for the service add it to the load balancer:
16261626+16271627+```yaml
16281628+services:
16291629+ myService:
16301630+ # ...
16311631+ labels:
16321632+ # ...
16331633+ traefik.http.services.serviceA.loadbalancer.server.port: 443
16341634+ traefik.http.services.serviceA.loadbalancer.serverstransport: ignorecert@file
16351635+ traefik.http.services.serviceA.loadbalancer.server.scheme: https
16361636+```
16371637+{: file="compose.yaml"}
1417163814181639### Multiple Services, Same Container
1419164014201420-Not an issue but not well documented
14211421-14221422-* Traefik will complain if multiple routers on container labels but only one service
14231423-* Can set service for router on label explicitly
14241424- * Docs examples focus on service name being implicitly define
14251425-Ex
16411641+This is not a well documented feature. For [routing configuration with labels](https://doc.traefik.io/traefik/providers/docker/#routing-configuration-with-labels) a service can have multiple routers but use the same [Service](https://doc.traefik.io/traefik/routing/services/) as long as the Service name is explicitly defined for each Router:
1426164214271643```yaml
14281428-# ...
16441644+services:
16451645+ myService:
16461646+ # ...
14291647 labels:
14301430- traefik.http.routers.mastodon.service: mastodon
14311431- #...
14321432- traefik.http.routers.mastodon-root.service: mastodon
16481648+ traefik.http.routers.myRouterA.service: serviceA
16491649+ traefik.http.routers.myRouterA.rule: Host(`service.example.com`)
16501650+ traefik.http.routers.myRouterA.entrypoint: entryA
16511651+ traefik.http.routers.myRouterB.service: serviceA
16521652+ traefik.http.routers.myRouterB.rule: Host(`service.example.org`)
16531653+ traefik.http.routers.myRouterB.entrypoint: entryB
14331654 # ...
14341434- traefik.http.services.mastodon.loadbalancer.server.port: 443
16551655+ traefik.http.services.serviceA.loadbalancer.server.port: 443
14351656```
16571657+{: file="compose.yaml"}
1436165814371659### Swarm and Overlay
14381660