202504030034 Replacing Metallb + Caddy with Tailscale Ingress as Private NLB Provider and Certs Resolver
Current Architecture
- External Reverse Proxy as a VM (let’s call it Tailscale Caddy)
- VM is a Caddy Reverse Proxy + Tailscale
- Cloudflare Public DNS points an A record to Tailscale Caddy private Tailscale Ipv4 address
*.app.mydomain.com-><your-tailscale-private-ip
- (Optional) I configured Adguard home to do DNS rewrites locally so all wildcard requests
*.app.mydomain.comgets rerouted to a local private IP. This IP, i.e.10.0.10.10is an IP assigned bymetallbas my bare-metal load balancer.- Why I chose metallb is that I needed a way to provide a HA way of provisioning IP addresses.

New Implementation
We will use Tailscale Kubernetes Operator, which we already am for Egresses (cluster to other tailnet devices).
We will use Cloudflare as our cert resolver and its DNS challenge method. With that, we need to create an API token and store it in the cluter which Traefik can use to do the challenges and request for certs.
- Uninstall
traefikin the cluster - Re-install
traefikvia helm chart and configure configurations- Configure
chownviainit-container - Store
CF_DNS_API_TOKENin the cluster
- Configure
- Configure a Tailscale Ingress for
traefikservice- In the chart configuration (or values.yaml), set
service.spec.loadbalancertotailscale - Tailscale Ingress will assign
traefikservice an external tailnet + private IP instead of metallb assigning from the configured pool.
- In the chart configuration (or values.yaml), set
- Find the Tailscale Ingress provisioned external IP names for the
traefik servicewithkubectl get svc -n <namespace> traefik - Add in External IP names for traefik into the public DNS (cloudflare) as an A Record.
- I do mine via Terraform
- Delete any AGH DNS Rewrites
- Create IngressRoute objects to configure traefik routing for the applications, ensuring they have the correcct
spec.entrypoints,spec.routes.matchandtls.certResolverandtls.domains- Each app = 1 IngressRoute
- Configure Tailscale ACL to allow devices access to the Tailscale Ingress pod
- Set ACL for
srcto the ingress podtagwhich can be found in your Tailscale Admin Console
- Set ACL for
- Re-deploy Traefik
- Traefik will begin requesting for TLS certs from
lets-encrypt

Benefits
This new implementation is far simpler and easier to make changes.
- All changes are done via Kubernetes
- Routes are done via
IngressRouteCRD- Declaring routes for apps become more declarative and in a “kubernetes-way” and follows closely to IaC concepts
- We no longer need to manage a
CaddyFile - We no longer need a separate VM to do reverse proxy, reducing surface attck and also minimzing overhead for resource management
- Routes are done via
- Certs are managed via CloudFlare automatically, with Traefik using Lets-Encrypt as a drop-in replacement for cert-manager which feels more lightweight
- Using Traefik as the ingress controller without a separate VM “fronting” it makes the solution more HA.
- I no longer have a dependency on the Caddy server to provision the HTTPs certs and routing
- I can deploy and provision routes in a “HA-way” and deploy a multi-replica, self-healing instance to configure routes, the way we want to do it with Kubernetes
- The architecture becomes far more simple