Files
Debian 01bd921ced feat: add home-assistant-dashboard-conventions skill + update multiple skills
- New: smart-home/home-assistant-dashboard-conventions (Mushroom cards, view tabs, no Bubble Cards)
- Updated: rke2, ceph, galera, proxmox, brainstorming, compound-learning, 1password-cli, smart-home-automation skills
- New references: ceph-cluster-administration, docker-volume-forensics, ceph-crush-weight, ceph-ec-mixed-size
2026-07-14 18:35:16 +00:00

5.2 KiB
Raw Permalink Blame History

Traefik LXC → K8s Migration (2026-07-14)

CT99999 Inventory (Source)

  • Location: proxmox7, VLAN 60, IP 10.0.60.10/24
  • Traefik version: 3.4.1
  • Static config: /etc/traefik/traefik.yaml
  • Dynamic config: /etc/traefik/conf.d/ (file provider, watch=true)
  • TLS: Let's Encrypt via TLS challenge, acme.json at /etc/traefik/ssl/acme.json
  • Entry Points: web (:80 → redirect to :443), websecure (:443), traefik (:8080 dashboard)

Services on CT99999

Router Host Backend Port Notes
grafana grafana.familie-schoen.com 10.0.30.141 3000 CT141 monitoring
librechat chat.familie-schoen.com, chat.schoen.eu 10.0.30.102 8080 LibreChat VM
seafile_old seafile.familie-schoen.com 10.0.30.100 8081 Seafile old
imkerei imkerei.familie-schoen.com 10.0.30.100 80 Catchall forwarder
immich immich.familie-schoen.com 10.0.30.99 2283 Immich
paperless dokumente.familie-schoen.com 10.0.30.101 8000 CT104 paperless
homeassistant homeassistant.familie-schoen.com 10.0.30.10 8123 VM106 HA
seafile cloud.familie-schoen.com 10.0.30.99 80 Seafile
seadoc cloud.familie-schoen.com + /sdoc-server 10.0.30.99 8888 SeaDoc, stripPrefix middleware
authelia auth.familie-schoen.com 192.168.100.11 9091 CT112 authelia
authelia-admin auth-admin.familie-schoen.com 192.168.100.11 9092 CT112 authelia admin
gitea git.familie-schoen.com 10.0.30.105 3000 CT108 (being replaced by K8s)
catchall-tcp HostSNI(*) passthrough 10.0.30.100 80 TCP catchall

K8s Traefik (Target)

  • Type: DaemonSet rke2-traefik in kube-system
  • Ports: hostPort 80 + 443 on all 6 nodes (10.0.30.51-53, .61-63)
  • IngressClass: traefik
  • Existing Ingresses: argocd-server (argocd ns), gitea (gitea ns)
  • Needs: Let's Encrypt certResolver (not configured by default)

HA Failover VIP via Cilium L2 (Implemented 2026-07-14)

Architecture

Instead of pointing the firewall NAT at a single K8s node (SPOF), a LoadBalancer Service requests a VIP from the existing Cilium LB IPAM pool. Cilium announces the VIP via ARP on one node; auto-failover to another node on failure (~1-2s).

Firewall NAT → 10.0.30.203 (Cilium VIP)
                    ↓ L2 Announcement (ARP)
              [aktiver K8s Node — Traefik DaemonSet]
              Automatisches Failover bei Node-Ausfall (~1-2s)

Components Deployed

Component File Purpose
Service (LoadBalancer) clusters/main/proxy/traefik-lb-service.yaml VIP .203, selects Traefik pods
Namespace clusters/main/proxy/namespace.yaml Shared proxy namespace
ArgoCD App clusters/main/apps/proxy.yaml GitOps for proxy namespace

traefik-lb-service.yaml

apiVersion: v1
kind: Service
metadata:
  name: traefik-lb
  namespace: kube-system
spec:
  type: LoadBalancer
  loadBalancerIP: 10.0.30.203
  selector:
    app.kubernetes.io/instance: rke2-traefik-kube-system
    app.kubernetes.io/name: rke2-traefik
  ports:
    - name: web
      port: 80
      targetPort: web
    - name: websecure
      port: 443
      targetPort: websecure

Why Cilium L2 (not MetalLB/keepalived)

  • Cilium L2 LB IPAM already running (3 services use it: .200, .201, .202)
  • No additional software needed
  • Automatic failover — no manual VIP management
  • Pool: 10.0.30.200250 (CiliumLoadBalancerIPPool default-pool)
  • L2 policy: default-l2-policy announces LB IPs via ARP on ^eth[0-9]+

Verification

After deploying, tested from mgmt-runner (.124):

  • 10.0.30.203:80/443 → 404 (no default route — correct)
  • git.schoen.codes via VIP → Gitea 1.26.1
  • argocd via VIP → ArgoCD UI

Firewall Cutover

Change NAT target: 10.0.60.1010.0.30.203 (one rule, instant cutover). Rollback: revert NAT to 10.0.60.10. CT99999 stays running throughout.

Pattern for External Backends (not in K8s)

Use ExternalName Service + IngressRoute CRD:

apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
  name: grafana
  namespace: proxy
spec:
  entryPoints:
    - websecure
  routes:
    - match: Host(`grafana.familie-schoen.com`)
      kind: Rule
      services:
        - name: grafana-external
          port: 3000
  tls:
    certResolver: letsencrypt
---
apiVersion: v1
kind: Service
metadata:
  name: grafana-external
  namespace: proxy
spec:
  type: ExternalName
  externalName: 10.0.30.141

SeaDoc StripPath Middleware

middlewares:
  seadoc-strippath:
    stripPrefix:
      prefixes:
        - /sdoc-server

Accessing CT99999

# CT99999 is on proxmox7 (10.0.20.70)
ssh -i ~/.ssh/id_ed25519_proxmox root@10.0.20.70 "pct exec 99999 -- ..."

Remaining Steps

  1. Add Let's Encrypt certResolver via HelmChartConfig for rke2-traefik
  2. Create IngressRoute + ExternalName Service for each of the 11 external backends
  3. Handle catchall-TCP (make specific, not HostSNI(*) — see pitfall in SKILL.md)
  4. Cutover firewall NAT: 10.0.60.1010.0.30.203
  5. Verify each service, then decommission CT99999 after 1 week

Reference

Full migration plan: docs/plans/2026-07-14-traefik-ct99999-k8s-migration.md in iac-homelab repo.