traefik Runbook¶
Metadata¶
| Field | Value |
|---|---|
| Service | traefik |
| Criticality | Tier 0 |
| Owner | Platform / Networking owner |
| Namespace | traefik |
| Clusters | homelab, local, jls, ozilab (Ozilab), cc-wrk-kube |
| Last validated | 2026-04-17 |
| Related service page | ../services/traefik.md |
Trigger Conditions¶
- External services become unreachable or return TLS errors.
- Dashboard URL stops responding or enters an auth loop.
- LoadBalancer external IP is missing or wrong.
- Let's Encrypt renewals fail or certificates expire unexpectedly.
1. Health Checks¶
Use these commands first to establish scope.
kubectl -n traefik get deploy,ds,svc,pod,pvc,ingressroute,middleware,tlsoption,tlsstore
kubectl -n traefik get pods -o wide
kubectl -n traefik describe pod -l app.kubernetes.io/app=traefik
kubectl -n traefik logs deploy/traefik --tail=200
kubectl -n traefik get svc traefik -o wide
Probe verification¶
Traefik uses /ping for readiness and liveness on port 80.
kubectl -n traefik describe pod <traefik-pod-name>
kubectl -n traefik port-forward deploy/traefik 9000:80
curl -I http://127.0.0.1:9000/ping
Record:
- whether the pod is failing readiness, liveness, or both
- whether /ping is healthy but the Service lacks a LoadBalancer IP
- whether a middleware or certificate issue is the actual user-facing problem
cc-obs-kube Jelastic worker ports¶
The jls overlay runs a host-network DaemonSet on the three untainted
workers. Its Service is ClusterIP; Pending external IP and MetalLB checks do
not apply to this overlay. The managed API enforces NodePorts 30000-32767,
so do not try to allocate NodePorts 80/443 or change provider API settings.
kubectl --context cc-obs-kube -n traefik get ds,pods,svc,pvc -o wide
kubectl --context cc-obs-kube -n traefik rollout status ds/traefik
kubectl --context cc-obs-kube -n traefik logs ds/traefik --tail=100
kubectl --context cc-obs-kube get nodes
# From a host on the worker network:
for worker in 172.16.8.168 172.16.8.174 172.16.8.170; do
curl --fail --connect-timeout 3 "http://$worker/ping"
done
Validate HTTPS with SNI for auth.mutana.site against each worker and the
approved Cloudflare Origin CA trust chain. An isolated curl --insecure
test confirms routing and TLS reachability only; it does not validate trust.
Then test through the actual public entry point. Direct worker checks alone
do not prove provider forwarding or public DNS is correct. On 2026-10-20 the
owner corrected Cloudflare DNS to the current cluster public IP; the public
auth endpoint then returned 200 with normal client TLS verification. When
using Jelastic shared load balancers instead of an environment public IP,
the provider also requires the custom domain to be bound to the environment.
Do not assume the provider's shared public domain tests a dedicated-IP path.
Check for competing listeners on ports 80/443 before rollout. The DaemonSet
uses ClusterFirstWithHostNet DNS. Keep the control-plane taint and validate
the existing NFS PVC on each worker. Current routes use the Cloudflare Origin
certificate, and acme.json was zero bytes at the 2026-10-20 recovery.
Do not activate ACME routes while multiple instances share that file; use a
single-writer certificate design first. Do not print the file contents.
For migration, stage and verify the DaemonSet before allowing Fleet to prune
the old Deployment. Keep traefik-pvc, configuration and Secret names intact.
Rollback by reverting the jls ingress change through the recovery branch:
Fleet recreates the previous Deployment and removes the DaemonSet. Verify
the Deployment and PVC before changing provider forwarding. That old revision
uses a LoadBalancer and again requires an approved external allocation; it
does not preserve worker-port exposure.
cc-wrk-kube routes (local -> 46.30.203.187)¶
A host served from cc-wrk-kube passes through local Traefik, HAProxy, and the worker DaemonSet. Check them in that order:
# local: route, transport, and pinned certificate exist
kubectl --context oci -n traefik get ingressroute,serverstransport,svc,cm cc-wrk-kube cc-wrk-kube-origin-cert
kubectl --context oci -n traefik logs deploy/traefik --tail=200 | grep -i cc-wrk-kube
# cc-wrk-kube: one ready pod per worker, TLS Secret present
kubectl --context cc-wrk-kube -n traefik get ds,pods -o wide
kubectl --context cc-wrk-kube -n traefik get secret cloudflare-origin-cert
- 502 or "certificate signed by unknown authority" on local: cc-wrk-kube is not serving the pinned origin certificate (Secret missing, or TLSStore not applied).
- 404 from cc-wrk-kube: no IngressRoute there for the Host header.
- Request handled by local instead of cc-wrk-kube: a local IngressRoute still matches the host (it outranks the priority-1 catch-all); delete it.
- 421 Misdirected Request: the cc-wrk-kube IngressRoute sets
tls.options; remove it. - Connection refused or reset: HAProxy has no healthy worker backend, or its source ACL does not include the local node that egresses.
2. Troubleshooting Workflows¶
LoadBalancer has no external IP or traffic never reaches Traefik¶
kubectl -n traefik get svc traefik -o yaml
kubectl -n metallb-system get ipaddresspools,l2advertisements
kubectl -n metallb-system logs deploy/controller --tail=200
kubectl -n metallb-system logs daemonset/speaker --tail=200
Check:
- Service type is still LoadBalancer in the active overlay
- the requested address is inside the active MetalLB pool
- MetalLB controller and speaker are healthy
Pod restarts or Traefik fails to boot¶
kubectl -n traefik logs deploy/traefik --previous
kubectl -n traefik describe pod <traefik-pod-name>
kubectl -n traefik get configmap traefik-config -o yaml
Check:
- invalid static or dynamic config rendered into traefik-config
- missing provider secrets such as cloudflare or do-auth-token
- PVC mount issues on traefik-pvc
Ozilab Fleet reports a missing local Secret input¶
Fleet must not depend permanently on a gitignored generator input. The Ozilab migration uses two reconciliations so Fleet cannot delete the Secrets it previously generated:
kubectl -n traefik get secret cloudflare basic-auth-user \
-o custom-columns=NAME:.metadata.name,KEEP:.metadata.annotations.helm\\.sh/resource-policy
Check:
- during phase 1, both Secrets exist and show
keepbefore generator inputs are removed from Git - after phase 2, both Secrets still exist but are maintained out of band
traefik/overlays/ozilabrenders without any local secret file after phase 2- the Fleet BundleDeployment no longer reports a missing generator source
If either annotation is absent, stop the phase-2 rollout. Restore the phase-1 revision, let Fleet reconcile it, and verify again without printing Secret data.
Cloudflare returns HTTP 525 for one host¶
525 is a TLS handshake failure between Cloudflare and the origin, so the request never reaches the backend and the app's own logs stay silent. Compare the failing host against a working one on the same edge IP:
for sni in <failing-host> <working-host>; do
echo "== $sni"
openssl s_client -connect <edge-ip>:443 -servername "$sni" </dev/null 2>&1 \
| grep -E "subject=|alert|SSL handshake has read"
done
tlsv1 unrecognized name (alert 112) on the failing SNI only means Traefik has
no certificate for that name and is not allowed to fall back to the default one:
Check:
- the route uses a bare
tls: {}onlocalandcc-wrk-kube. AcertResolveror a TLS option withsniStrict: true(modern@file,intermediate@file,old@file) disables the default-certificate fallback, and those two clusters serve every host from the default TLSStore - if a
certResolveris intended, it exists in that cluster'straefik-static-config.yamland has actually issued the certificate:kubectl -n traefik exec deploy/traefik -- cat /certs/acme.jsonwith"Certificates": nullmeans nothing was ever issued - the deployed manifest, not just the Kustomize source. Workloads whose
fleet.yamlpoints at anoverlays/<cluster>/fleetdirectory are served from that copy, which can drift from the overlay next to it
Certificate issuance or renewal fails¶
kubectl -n traefik logs deploy/traefik --tail=400 | grep -i acme
kubectl -n traefik get secret cloudflare do-auth-token cloudflare-origin-cert
kubectl -n traefik get pvc traefik-pvc
Check:
- DNS provider token secret is present and correct
- ACME storage on traefik-pvc is writable
- certResolver names in IngressRoute or TLS config still match the active static configuration
- the route is on a cluster that has resolvers at all:
localandcc-wrk-kubedeclare none and expect a baretls: {}
Dashboard route fails or redirects endlessly through auth¶
kubectl -n traefik get ingressroute traefik-dashboard -o yaml
kubectl -n traefik get middleware
kubectl -n auth get pods
kubectl -n traefik logs deploy/traefik --tail=200 | grep -i auth
Check:
- dashboard host matches the overlay you deployed
- Authelia is reachable on the expected internal address
- middleware reference uses the correct provider namespace or suffix for the active overlay
3. Disaster Recovery¶
Preconditions¶
- Confirm whether the outage is caused by MetalLB reachability, Traefik config, or provider credential issues.
- Identify the active overlay for the affected cluster.
- Verify the required secret source files and PVC are available.
- For Ozilab, verify the out-of-band
cloudflareandbasic-auth-userSecrets exist; do not expect Fleet to load plaintext inputs from Git after phase 2.
Stateful workload recovery¶
Traefik state is small but important because ACME data lives on a PVC.
- Preserve or snapshot traefik-pvc when possible before destructive actions.
- Restore the active overlay secrets and config files.
- Reapply the correct overlay with Kustomize.
- Restore the PVC if ACME state was lost and certificate continuity matters.
- Validate /ping, external IP assignment, and dashboard access.
Cluster rebuild dependency order¶
- Cluster networking and MetalLB
- Namespace traefik and required secrets
- traefik-pvc or replacement certificate strategy
- Traefik deployment and CRDs
- Protected routes and dashboard validation
4. Scaling and Resource Management¶
Preferred path: adjust the overlay manifests and roll through GitOps.
Use these commands to size the problem before changing resources:
kubectl -n traefik top pod
kubectl -n traefik get deploy traefik -o yaml
kubectl -n traefik describe deploy traefik
Record:
- sustained CPU or memory pressure on the ingress pod
- request volume versus access log behavior
- whether scaling above one replica would conflict with the current ReadWriteOnce ACME storage model
Current guidance: do not increase replicas casually while the ACME state is stored on a single PVC.
5. Maintenance Procedures¶
- Rotate DNS provider tokens.
- Rotate dashboard credentials and origin certificates.
- Review Traefik image and CRD version alignment before upgrades.
- Validate dashboard hostnames and cross-cluster ExternalName routes after overlay changes.
For Ozilab, set kubectl to the target cluster and bootstrap or rotate the two
Secrets using the commands in traefik/README.md. Then either let Fleet
reconcile traefik/overlays/ozilab from branch dev, or deploy the same overlay
manually:
Do not create a Fleet-only Kustomization or copy the workload manifests for the manual path.
For each task, define:
- Preconditions: active overlay identified and backup of provider secrets available
- Impact window: possible certificate or routing disruption during rollout
- Rollback path: revert overlay revision and reapply previous secrets
- Validation steps: /ping, dashboard route, and one representative service route succeed
6. Rollback Strategy¶
Document the fastest safe rollback path:
- Revert the affected overlay revision.
- Restore the previous secretGenerator source files.
- If ACME state was corrupted, restore the previous traefik-pvc snapshot or delete the bad state and allow re-issuance when acceptable.
7. Post-Incident Actions¶
After recovery, always:
- Update CHANGELOG.md if traffic was manually rerouted, certificates were restored, or secrets were rotated outside the normal workflow.
- Update the Traefik service page if hosts, middleware chains, provider strategy, or metrics wiring changed.
- Update this runbook if the incident revealed a missing auth, certificate, or MetalLB diagnostic step.
- Capture any follow-up work to remove legacy prod overlay assumptions.