Baseline Setup, Part 3 – Dynamic Routing with Traefik
How I deployed Traefik in my MicroK8s cluster using static and dynamic configs. With per-service TOML files, Let's Encrypt, and zero drama, Traefik remains a cornerstone in my homelab setup.
With the cluster online and MetalLB assigning IPs like a champ, the next step was clear: routing.
This is where Traefik comes in. And not for the first time.
I’ve used Traefik in my last two homelab setups, and honestly? It hasn’t failed me yet. It’s one of the few pieces in my stack I never really second-guess. It just works—honestly kind of like magic.
This post walks through how I’ve got Traefik deployed in MicroK8s, how I use file-based TOML configs for each app, and why this setup makes reverse proxying feel way less chaotic.
⚙️ Why Traefik Again?
Because it keeps earning its spot.
A few things I still love about it:
- Dynamic service discovery via Kubernetes or static files
- Built-in Let’s Encrypt with zero drama
- TOML configs that are readable and modular
- A dashboard that actually helps
- Works perfectly behind MetalLB
It’s handled dozens of self-hosted services for me across two full rebuilds, and I’ve never had to rage-Google it in the middle of the night. That’s saying something.
🚀 How I Deployed It
I’m running Traefik in its own namespace (traefik
) inside MicroK8s. Deployment is via Helm, customized with static and dynamic config split cleanly.
Here’s the static config (traefik.yaml
), mounted via ConfigMap:
apiVersion: v1
kind: ConfigMap
metadata:
name: traefik-config
namespace: traefik
data:
traefik.yaml: |
api:
dashboard: true
insecure: false
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"
certificatesResolvers:
letsencrypt:
acme:
email: your-email@example.com
storage: /letsencrypt/acme.json
httpChallenge:
entryPoint: web
This gives me HTTP→HTTPS redirects, automated certs, and access to the Traefik dashboard (only exposed on my LAN) for troubleshooting.
🗂 Dynamic Config: One File Per Service
This is my favorite part of the setup.
Every route is its own TOML file, stored in a shared folder and mounted into the Traefik container. Here's a typical example for something like Frigate:
[http.routers.frigate]
rule = "Host(`frigate.petieclark.com`)"
entryPoints = ["websecure"]
service = "frigate"
tls = true
[http.services.frigate.loadBalancer]
[[http.services.frigate.loadBalancer.servers]]
url = "http://frigate.frigate.svc.cluster.local:5000"
Drop the file in, restart the deployment, and it’s live.
Files live at:/mnt/data/sundarideploy/services/traefik/dynamic/
🔒 Let’s Encrypt TLS, No Fuss
Traefik handles TLS termination via Let’s Encrypt with zero Certbot, no cron jobs, no custom scripts. Just:
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
It even renews certs on its own and logs them to a persistent acme.json
.
✅ Testing New Routes
Here's my deploy flow for a new service:
- Drop the TOML in the dynamic config dir
- Open
/dashboard
and confirm the router shows up - Hit the service in-browser (or with
curl
) to confirm routing
Run:
microk8s kubectl rollout restart deployment/traefik -n traefik
🧠 Lessons Learned
- Use TOML for dynamic routes. It scales better than big YAML files.
- Traefik’s dashboard is legit helpful—bookmark it.
- Mount your
acme.json
so certs persist between restarts. - Keep per-service config files version-controlled if possible.
🔜 Next Up: Authentik
With routing online, it’s time to lock things down. In Part 4, I’ll show how I deployed Authentik to centralize authentication and give every service SSO—without rewriting a million config files.