[READ-ONLY] Mirror of https://github.com/FoxxMD/blog. blog.foxxmd.dev
0

Configure Feed

Select the types of activity you want to include in your feed.

feat(traefik_migrate): Fill out FAQ

FoxxMD (Apr 15, 2025, 9:59 PM UTC) 53ac941b 8ea18df8

+257 -35
+257 -35
_posts/2025-04-07-migrating-to-traefik.md
··· 81 81 * Has first-class docker service discovery via labels and supports [multiple hosts](https://github.com/yusing/godoxy/wiki/Configurations#setting-up-providers) 82 82 * 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 83 83 84 - ## Satifying Requirements with Traefik 84 + ## Satisfying Requirements with Traefik 85 85 86 86 I eventually settled on [Traefik](https://traefik.io/) after due-diligence gave me enough confidence to think I could satisfy all of the [Requirement](#requirementsspec). 87 87 ··· 93 93 94 94 This was the hardest requirement to meet for **edge cases**. Unlike NGINX, 95 95 96 - * Chaining together multiple transformations of a route is not as straightforward 97 96 * Doing anything that short-circuits a route and isn't already a middleware isn't well documented 98 97 * Traefik doesn't have as many middlewares (plugins) and well-documented, non-trivial "how to do X" examples 99 98 * 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. ··· 267 266 268 267 #### How do I do X? 269 268 270 - Check the [FAQ](#faq-and-how-tos) at the bottom for more examples like 269 + Check the [FAQ](#faq) at the bottom for more examples like 271 270 272 271 * [Chaining middleware](#chaining-middleware-with-non-trivial-examples) 273 272 * [Redirect on non-existent Route](#redirect-on-non-existent-route) ··· 1377 1376 ``` 1378 1377 {: file="static_config/traefik.yaml" } 1379 1378 1380 - ## Traefik Gotchas 1379 + ## FAQ, Gotchas, and How To's {#faq} 1381 1380 1382 - ### Ambiguous Config Sources and Documentation 1381 + ### Static/Dynamic Config Explained 1383 1382 1384 - * Difficult to easily grok what the difference between static/dynamic config is 1385 - * Difficult to determine where static/dynamic config should be placed in dir/files 1386 - * Not obvious that config from separate sources can be used anywhere (file middleware in labels, docker plugin config in files) 1387 - * Parsed config sources not listed anywhere by name 1383 + 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. 1384 + 1385 + #### Static Config 1386 + 1387 + [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. 1388 + 1389 + 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.** 1390 + 1391 + Static Configuration can be set in three places: 1392 + 1393 + ##### File {#static-file-reference} 1394 + 1395 + In a [**specific configuration file**](https://doc.traefik.io/traefik/getting-started/configuration-overview/#configuration-file): 1396 + 1397 + * `/etc/traefik/traefik.yaml` 1398 + * `$HOME/.config/traefik.yaml` 1399 + * `./traefik.yaml` (the working directory) 1400 + * a file defined as an argument to the traefik program IE `traefik --configFile=foo/bar/myconfigfile.yml` 1401 + 1402 + **Dynamic and Static Configuration files cannot be mixed.** 1403 + 1404 + [**File Reference**](https://doc.traefik.io/traefik/reference/static-configuration/file/) 1405 + 1406 + ##### CLI {#cli-cli-reference} 1407 + 1408 + 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: 1409 + 1410 + ```shell 1411 + traefik --providers.docker=true --providers.docker.exposedbydefault=false --providers.file.directory=/etc/traefik/dynamic 1412 + ``` 1413 + 1414 + ```yaml 1415 + traefik: 1416 + image: "traefik:v3.3" 1417 + command: 1418 + - "--providers.docker=true" 1419 + - "--providers.docker.exposedbydefault=false" 1420 + - "--providers.file.directory=/etc/traefik/dynamic" 1421 + # ... 1422 + ``` 1423 + 1424 + 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.** 1425 + 1426 + [**CLI Reference**](https://doc.traefik.io/traefik/reference/static-configuration/cli/) 1427 + 1428 + ##### ENV {#static-env-reference} 1429 + 1430 + 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.** 1431 + 1432 + [**ENV Reference**](https://doc.traefik.io/traefik/reference/static-configuration/env/) 1433 + 1434 + #### Dynamic Configuration 1435 + 1436 + [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... 1437 + 1438 + **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). 1439 + 1440 + 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: 1441 + 1442 + ```yaml 1443 + providers: 1444 + file: 1445 + directory: "/config" 1446 + ``` 1447 + {: file="/etc/traefik/traefik.yaml"} 1448 + 1449 + **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. 1450 + 1451 + ### Static/Dynamic Config Best Practices 1452 + 1453 + 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. 1454 + 1455 + Whatever you end up doing, it's best to keep similar features together: 1456 + 1457 + * Don't define 3 plugins in static file and 2 in CLI 1458 + * Do define one-use middleware in the same docker labels as the serivce using it 1459 + * 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. 1460 + 1461 + #### Static Config In Files {#static-file} 1462 + 1463 + 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. 1464 + 1465 + ```yaml 1466 + command: 1467 + - "--entryPoints.web.address=80" 1468 + - "--entryPoints.web.http.middlewares=rate-limit@file" 1469 + - "--entryPoints.web.forwardedHeaders.trustedIPs[0]=172.28.0.1/24" 1470 + - "--entryPoints.web.forwardedHeaders.trustedIPs[1]=172.20.0.1/24" 1471 + ``` 1472 + vs 1473 + ```yaml 1474 + web: 1475 + address: :80 1476 + http: 1477 + middlewares: 1478 + - rate-limit@file 1479 + forwardedHeaders: 1480 + trustedIPs: 1481 + - 172.28.0.1/24 1482 + - 172.20.0.1/24 1483 + ``` 1484 + 1485 + I keep my static config mounted to `/etc/traefik/traefik.yaml` inside the traefik docker container. 1486 + 1487 + ```yaml 1488 + traefik: 1489 + image: "traefik:v3.3" 1490 + # ... 1491 + volumes: 1492 + - /host/path/to/static_dir:/etc/traefik 1493 + ``` 1494 + {: file="compose.yaml"} 1495 + 1496 + #### Dynamic Config In Labels {#dynamic-file} 1497 + 1498 + 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. 1499 + 1500 + 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`). 1501 + 1502 + The [File provider](https://doc.traefik.io/traefik/providers/file/) is configured to parse dynamic configs from `/config` mounted into the traefik container. 1503 + 1504 + ```yaml 1505 + providers: 1506 + file: 1507 + directory: "/config" 1508 + ``` 1509 + {: file="/etc/traefik/traefik.yaml"} 1510 + 1511 + ```yaml 1512 + traefik: 1513 + image: "traefik:v3.3" 1514 + # ... 1515 + volumes: 1516 + - /host/path/to/static_dir:/etc/traefik 1517 + - /host/path/to/dynamic_dir:/config 1518 + ``` 1519 + {: file="compose.yaml"} 1520 + 1521 + #### Reusable Dynamic Config In Files {#dynamic-label} 1522 + 1523 + 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. 1524 + 1525 + ```yaml 1526 + services: 1527 + serviceA: 1528 + # ... 1529 + labels: 1530 + traefik.enable: true 1531 + traefik.http.routers.serviceA.rule: Host(`serviceA.example.com`) 1532 + traefik.http.services.serviceA.loadbalancer.server.port: 3000 1533 + traefik.docker.network: internal_overlay 1534 + ``` 1535 + {: file="compose.yaml"} 1388 1536 1389 1537 ### Finding Config Errors 1390 1538 1391 - * Dynamic config issues immediate feedback, visible in dashboard 1392 - * Static config does not appear until restart and traefik will bulldoze over silently 1393 - * Errors only show in docker logs but traefik will startup anyways 1394 - * May cause dashboard to entirely disappear if error affects internal routes 1395 - * Errors may look like dynamic if the error trickles downstream 1396 - * So..always check docker logs **first** and always restart traefik after any static config changes 1539 + 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: 1540 + 1541 + * Does not appear until restart and traefik will bulldoze over silently 1542 + * Errors only show in docker logs but traefik will startup anyways 1543 + * May cause dashboard to entirely disappear if error affects internal routes 1544 + * Errors may look like dynamic if the error trickles downstream 1545 + 1546 + 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. 1397 1547 1398 1548 ### Clobbering Labels 1399 1549 1400 - * Copy-pasting labels, may forget service/router name is same as existing 1401 - * Traefik does not complain about this unless it causes actually issues (router issue) but will still cause reachability issues while running 1550 + This is mainly applicable when using [routing configuration with labels (Docker compose labels)](https://doc.traefik.io/traefik/providers/docker/#routing-configuration-with-labels). 1402 1551 1403 - ## FAQ and How To's 1552 + 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: 1404 1553 1405 - ### Chaining Middleware with Non-Trivial Examples 1554 + ```yaml 1555 + services: 1556 + serviceA: 1557 + image: myFooImage 1558 + labels: 1559 + traefik.http.routers.myRouterA.rule: Host(`service.example.com`) 1560 + traefik.http.services.serviceA.loadbalancer.server.port: 8080 1561 + serviceB: 1562 + image: myBarImage 1563 + labels: 1564 + traefik.http.routers.myRouterA.rule: Host(`service.example.org`) 1565 + traefik.http.services.serviceA.loadbalancer.server.port: 8090 1566 + ``` 1406 1567 1407 - Mastodon example 1568 + 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!* 1569 + 1570 + So, 1571 + 1572 + * make sure that when copy-pasting services/labels that the names are changed 1573 + * if a service is not reachable and copy-pasting or referencing existing services was used check for label clobbering first before looking anywhere else 1408 1574 1409 1575 ### Redirect on non-existent Route 1410 1576 1411 - Return 302 instead of 404 for wildcare routes. 1577 + This configuration makes Traefik redirect to *any* site of your choice (not just an existing Service) if no other Route is matched. 1412 1578 1413 - ### Missing How Do To X Example 1579 + This needs to be done at least partially in a [dynamic config (File provider)]([dynamic config file](https://doc.traefik.io/traefik/providers/file/)). 1414 1580 1415 - * Minio for mastodon, `customresponseheaders` 1416 - * `ignorecert` and `insecureSkipVerify` usage 1581 + 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. 1582 + 1583 + Then, add a new [Router](https://doc.traefik.io/traefik/routing/routers/) to the dynamic config. Explanation: 1584 + 1585 + * `PathPrefix('/')` makes the router match any route 1586 + * `priority: 1` uses a low [priority](https://doc.traefik.io/traefik/routing/routers/#priority) to ensure the rule is run **after** any other Routers. 1587 + * `middlewares: anyreg` makes the route redirect (always) 1588 + * `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) 1589 + 1590 + ```yaml 1591 + http: 1592 + middlewares: 1593 + anyreg: 1594 + redirectregex: 1595 + regex: ^.* 1596 + replacement: https://example.com 1597 + routers: 1598 + catchall: 1599 + rule: "PathPrefix(`/`)" 1600 + priority: 1 1601 + middlewares: anyreg 1602 + service: noop@internal 1603 + entryPoints: 1604 + - yourEntryPoint 1605 + ``` 1606 + {: file="/config/global.yaml"} 1607 + 1608 + ### Trust Service Self-Generated Certificate 1609 + 1610 + 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. 1611 + 1612 + This needs to be configured in a [dynamic config (File provider)]([dynamic config file](https://doc.traefik.io/traefik/providers/file/)). 1613 + 1614 + 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): 1615 + 1616 + ```yaml 1617 + http: 1618 + # ... 1619 + serversTransports: 1620 + ignorecert: 1621 + insecureSkipVerify: true 1622 + ``` 1623 + {: file="/config/global.yaml"} 1624 + 1625 + Then, on the docker labels for the service add it to the load balancer: 1626 + 1627 + ```yaml 1628 + services: 1629 + myService: 1630 + # ... 1631 + labels: 1632 + # ... 1633 + traefik.http.services.serviceA.loadbalancer.server.port: 443 1634 + traefik.http.services.serviceA.loadbalancer.serverstransport: ignorecert@file 1635 + traefik.http.services.serviceA.loadbalancer.server.scheme: https 1636 + ``` 1637 + {: file="compose.yaml"} 1417 1638 1418 1639 ### Multiple Services, Same Container 1419 1640 1420 - Not an issue but not well documented 1421 - 1422 - * Traefik will complain if multiple routers on container labels but only one service 1423 - * Can set service for router on label explicitly 1424 - * Docs examples focus on service name being implicitly define 1425 - Ex 1641 + 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: 1426 1642 1427 1643 ```yaml 1428 - # ... 1644 + services: 1645 + myService: 1646 + # ... 1429 1647 labels: 1430 - traefik.http.routers.mastodon.service: mastodon 1431 - #... 1432 - traefik.http.routers.mastodon-root.service: mastodon 1648 + traefik.http.routers.myRouterA.service: serviceA 1649 + traefik.http.routers.myRouterA.rule: Host(`service.example.com`) 1650 + traefik.http.routers.myRouterA.entrypoint: entryA 1651 + traefik.http.routers.myRouterB.service: serviceA 1652 + traefik.http.routers.myRouterB.rule: Host(`service.example.org`) 1653 + traefik.http.routers.myRouterB.entrypoint: entryB 1433 1654 # ... 1434 - traefik.http.services.mastodon.loadbalancer.server.port: 443 1655 + traefik.http.services.serviceA.loadbalancer.server.port: 443 1435 1656 ``` 1657 + {: file="compose.yaml"} 1436 1658 1437 1659 ### Swarm and Overlay 1438 1660