I’ve been using Traefik as a reverse proxy and ingress controller for quite a while, and it’s one of my favorites. I like how I can colocate the networking configuration for my applications alongside my application configuration all in Compose spec files. However, it can get a bit exhausting to constantly repeat much of the same configuration over and over again. Here’s a collection of the things I’ve done to simplify my Traefik dynamic configurations by adding some defaults to the static configuration.

The defaults I use are all configured in the providers section. Here’s a snippet of my docker provider configuration:

  docker:
    exposedByDefault: false
    endpoint: "tcp://docker-socket-proxy:2375"
    watch: true
    defaultRule: "Host(`{{ index .Labels \"traefik.hostname\"}}.your.domain.com`)"
    network: "ingress"

Let’s walk through these settings:

  • exposedByDefault: false - This setting tells Traefik to ignore any container that doesn’t have a traefik.enable=true label. By default, Traefik’s going to try and proxy everything, which is almost never what I want. This does mean that I need to add the label traefik.enable: true to my frontend containers, but this label is alongside the rest of the traefik configuration labels, which makes it easy to remember.
  • endpoint: "tcp://docker-socket-proxy:2375" - This is the address of the Docker API that Traefik is going to use to watch for new containers. I use docker-socket-proxy, a web application firewall service that can add much needed security to the otherwise all-powerful Docker API when accessed via a direct socket connection. Giving any service connected to the internet access to a very powerful API seems like a pretty bad idea. This limits Traefik’s interactions to only being able to read the specific information required to do its job, minimizing the potential for a security vulnerability in Traefik to be able to use the Docker API to execute arbitrary code on the host.
  • watch: true - Pretty bog standard, and it’s why we’re using Docker labels in the first place. We want Docker’s API to dynamically inform Traefik of changes that are happening in the environment.
  • defaultRule: "Host({{ index .Labels "traefik.hostname"}}.your.domain.com)" - This one’s the biggest secret sauce for me. When paired with a wildcard domain record pointing to the address of my Traefik server, this allows me to use a custom Docker label (traefik.hostname), to inform Traefik what the hostname for the route should be. Yes, it’s not a huge deal to write all this out, and I now build all my services with Git templates that could bring a lot of this configuration in for me, but I still find this shorthand preferable. Traefik does allow you to override this or any default rule on a per-container/route basis, which means I’m not losing any flexibility here.
  • network: "ingress" - It’s pretty standard for a Docker based Traefik setup to have a common network that you put your frontend services on. I set up this network outside of any Compose configuration. Without specifying this (either in the provider or in the container labels), Traefik will try to use the first network in the container’s network list, regardless of whether Traefik has access to it or not. This can lead to some frustrating troubleshooting sessions (ask me how I know) when you’re trying to figure out why Traefik isn’t routing traffic to your service. This allows me to be less strict about how I define network attachments for a service in my Compose files.

Setting these up has made my Docker-powered ops life just a little easier. It’s nice to have a few less things to worry about when I’m spinning up a new service. Not having to open up other service configs to ask “how do I do this again?” and avoiding typos for boilerplate makes life quite nice.