- 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
5.2 KiB
5.2 KiB
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.jsonat/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-traefikinkube-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.200–250(CiliumLoadBalancerIPPooldefault-pool) - L2 policy:
default-l2-policyannounces 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.codesvia VIP → Gitea 1.26.1 ✅argocdvia VIP → ArgoCD UI ✅
Firewall Cutover
Change NAT target: 10.0.60.10 → 10.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
- Add Let's Encrypt certResolver via HelmChartConfig for
rke2-traefik - Create IngressRoute + ExternalName Service for each of the 11 external backends
- Handle catchall-TCP (make specific, not HostSNI(
*) — see pitfall in SKILL.md) - Cutover firewall NAT:
10.0.60.10→10.0.30.203 - 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.