Traefik and cloudflared on unraid

someone on discord asked about setting up photostructure with a traefik reverse proxy, to be used optionally with cloudflared. So I thought to share my setup. I am sure it’s not perfect, so I’d love to get feedback.

For the sake of privacy, I’ve anonymized my email as myemail@mydomain.net, API key, and domain name as mydomain.net. Obviously you’d replace with real values, including an API key generated from your cloudflare account.

The basic setup is the following:

  • photostructure container running on default port on my server at 192.168.1.4
  • I have a traefik 2.x proxy also running on 192.168.1.4, traefik is reverse proxying every application as a subdomain (i.e. photostructure is at https://ps.mydomain.net).
  • I have configured traefik to retreive a let’s encrypt wildcare SSL certificate for mydomain.net. Wildcard certificates require validation through DNS. Since I use cloudflare for my domain’s DNS, the instructions below are specific to cloudflare DNS.
  • I use cloudflare Tunnel client to expose photostructure to the internet. I am not going to get in full details on the cloudflared setup, since there are some good tutorials on that, but this allows me to put authentication around photostructure, and I don’t need to put port forwarding rules in my firewall.
  • My internal traffic does not go through cloudflared. I have an internal DNS server that map ps.mydomain.net to 192.168.1.4 (so directly to traefik). I happen to use pi-hole for DNS, but most routers’ DNS allow some means to have local DNS entries that would take precendence over the public DNS servers.

Traefik container

Setup your traefik (2.x) container with 2 bind mounts. These may be defaults already, depending on the unraid template.

container path: /etc/traefik → host path: /mnt/user/appdata/traefik
container path: /shared → host path: /mnt/user/appdata/traefik/shared

I configured these 4 variables. The first two are needed for DNS auth with cloudflare for the wildcard SSL certificate:

CF_API_EMAIL=myemail@mydomain.net
CF_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
traefik.enable=true
traefik.frontend.headers.SSLHost=mydomain.net

Traefik configuration files

Traefik allows you do everything through docker labels, so you don’t need to have configuration files if you don’t want. I chose to use the configuration file options for my instance, I find the labels option a bit messy and hard to debug, especially if you introduce SSL and middlewares. Traefik also supports multiple config file formats! I use TOML instead of YAML, again a choice
I made. Traefik’s documentation at Traefik Proxy Documentation - Traefik provides example of all 3 options.

My main traefik configuration file is the following . The key sections are the entrypoints (ports, I use standard http and https ports), the fact that I chose to use the file providers (basically one config for each application I proxy), and certificate resolver information.

/mnt/user/appdata/traefik/traefik.toml

[api]
   insecure = true
   dashboard = true
   debug = true
[log]
   filePath = "/shared/logs/log.json"
   format = "json"
[accessLog]
   filePath = "/shared/logs/access.json"
   format = "json"
   bufferingSize = 42
[entryPoints]
   [entryPoints.web]
      address = ":81"
      [entryPoints.web.forwardedHeaders]
          insecure = true
   [entryPoints.web-secure-int]
      address = ":443"
      [entryPoints.web-secure-int.forwardedHeaders]
          insecure = true
[providers]
   [providers.file]
      directory = "/shared/rules"
      watch = true
[certificatesResolvers]
   [certificatesResolvers.letse]
      [certificatesResolvers.letse.acme]
         email = "myemail@mydomain.net"
         storage = "/shared/acme/acme.json"
         [certificatesResolvers.letse.acme.dnsChallenge]
             provider = "cloudflare"

Every app that I reverse proxy has its own “rules” files in /mnt/user/appdata/traefik/shared/rules/ - all of the rules files follow the same structure, the only thing that changes are the router and service names, the domain name, and of course the endpoint URL being proxied. My rules file for photostructure is the following:

/mnt/user/appdata/traefik/shared/rules/photostructure.toml

[http.routers]
    [http.routers.photostructure]
      entryPoints = ["web-secure-int","web"]
      rule = "Host(`ps.mydomain.net`)"
      service = "photostructure"
      [http.routers.photostructure.tls]
        certResolver = "letse"
        [[http.routers.photostructure.tls.domains]]
          main = "mydomain.net"
          sans = ["*.mydomain.net"]
[http.services]
  [http.services.photostructure.loadBalancer]
    [[http.services.photostructure.loadBalancer.servers]]
      url = "http://192.168.1.4:1787/"

cloudflared container

Without getting into a lot of details on the cloudflare tunnel (much better tutorial on that on unraid site and/or youtube), here is the config.yaml for my cloudflared container. The key point here is that the ingress is pointing to https://192.168.1.4 for everything under mydomain.net. Basically it hands everything off to traefik.

tunnel: xxxxxxx-xxxx-xxxx-xxxxx-xxxxxxxxxxxxxxxxxx 
credentials-file: /home/nonroot/.cloudflared/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.json
# forward all traffic to Reverse Proxy w/ SSL
ingress:
  - service: https://192.168.1.4
    originRequest:
      originServerName: mydomain.net