Skip to content

Commit de8ec23

Browse files
web-flowclaude
andcommitted
docs: add Coral crashloop RCA and update migration/firmware runbooks
RCA for Frigate CrashLoopBackOff caused by liveness probe killing pods too fast, corrupting Coral USB TPU state. Documents the immutable Job Flux blockage and USB passthrough reset gotcha. Updated Ollama migration runbook with Flux immutable Job lesson. Updated Voice PE firmware runbook with 26.4.0 upgrade notes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent a6e0ba6 commit de8ec23

3 files changed

Lines changed: 76 additions & 0 deletions

File tree

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# RCA: Frigate CrashLoopBackOff - Coral USB TPU State Corruption
2+
3+
**Date**: 2026-04-05
4+
**Duration**: ~45 minutes
5+
**Impact**: All 5 cameras offline, no object detection, no face recognition, no "Hello G" greetings
6+
**Severity**: High
7+
8+
## Timeline
9+
10+
| Time (PT) | Event |
11+
|-----------|-------|
12+
| ~14:30 | HAOS VM 116 rebooted (chief-horse) for CPU core change (2→4) |
13+
| ~14:48 | HA came back, Frigate face sensors reset to "Unknown" |
14+
| ~15:10 | Frigate cameras confirmed working (Coral at 25ms) |
15+
| ~22:35 | Flux reconciliation blocked by immutable Ollama Job, preventing health checker image update |
16+
| ~22:57 | Frigate pod restarted, Coral USB TPU not detected |
17+
| ~22:57 | Liveness probe kills Frigate after 60s (API never starts without Coral) |
18+
| ~22:57-23:07 | CrashLoopBackOff - 6 rapid restarts corrupt Coral USB state further |
19+
| ~23:02 | USB reset from host attempted - disconnected Coral from VM passthrough |
20+
| ~23:02 | `qm set 105 -usb1` re-attached USB config but VM can't hotplug USB |
21+
| ~23:07 | Liveness probe increased to 180s initialDelay (committed via Flux) |
22+
| ~23:07 | VM 105 (pumped-piglet-gpu) rebooted to restore USB passthrough |
23+
| ~23:07 | Frigate starts, Coral TPU found, all cameras streaming |
24+
25+
## Root Cause
26+
27+
**Three contributing factors:**
28+
29+
1. **Aggressive liveness probe** (initialDelaySeconds=60) killed Frigate before it could fully start when the Coral was slow to initialize. Each kill triggered a pod restart.
30+
31+
2. **Rapid USB device cycling** from 6+ pod restarts in quick succession corrupted the Coral USB TPU state. The USB interface wasn't properly released between restarts, leaving the device in a "claimed but not accessible" state.
32+
33+
3. **USB reset from host detached VM passthrough**. Running `usbreset` on the Proxmox host disconnected the Coral from the VM's USB passthrough. `qm set` updated the config but USB hotplug doesn't work for passthrough devices — requires VM reboot.
34+
35+
**Secondary factor:** Flux reconciliation was blocked for 40+ minutes by an immutable Ollama Job (`ollama-model-update-gemma4`). This prevented the health checker image update from deploying, though the health checker wouldn't have prevented this specific issue.
36+
37+
## Fix Applied
38+
39+
1. **Liveness probe relaxed**: `initialDelaySeconds` 60→180, `failureThreshold` 3→5. Gives Frigate 3+ minutes to start before probes kill it.
40+
2. **VM rebooted** to restore Coral USB passthrough.
41+
3. **Immutable Ollama Job deleted** to unblock Flux reconciliation.
42+
43+
## Prevention
44+
45+
- Never run `usbreset` on a USB device that's passed through to a VM — it breaks the passthrough. Reboot the VM instead.
46+
- K8s Jobs are immutable once created. When renaming Jobs in GitOps manifests, delete the old Job before pushing.
47+
- The health checker CronJob now runs with a working image (`21-2f5fed2`) and will detect camera failures going forward.
48+
49+
## Lessons Learned
50+
51+
1. **Liveness probes are a blunt instrument for stateful hardware.** Coral USB TPU has state that survives pod restarts but corrupts under rapid cycling. The health checker (with grace periods and confirmation thresholds) is better suited for restart decisions.
52+
2. **USB passthrough ≠ USB access.** Host-level USB operations (reset, rebind) break VM passthrough. Always operate on USB devices from inside the VM, or reboot the VM.
53+
3. **Immutable K8s resources block Flux globally.** One failed Job reconciliation blocks ALL resources in the kustomization. Monitor `flux get kustomization` for `ReconciliationFailed` status.
54+
55+
## Related
56+
57+
- [Frigate health checker restart loop RCA](../runbooks/frigate-health-checker-restart-loop-rca.md)
58+
- [Coral USB TPU troubleshooting](../runbooks/frigate-tpu-troubleshooting.md)
59+
- [Ollama Gemma 4 migration runbook](../runbooks/ollama-gemma4-migration.md)
60+
61+
**Tags**: frigate, coral, tpu, usb, crashloop, liveness-probe, flux, immutable-job, gpu, pumped-piglet

docs/runbooks/ollama-gemma4-migration.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,13 @@ scripts/ollama/test-inference.sh gemma4:e2b "what time is it?"
9797
# Say "Hey Nabu, what's the weather like?" and confirm response <3s
9898
```
9999

100+
## Post-Migration Issues
101+
102+
### Flux Reconciliation Blocked by Immutable Job
103+
Renaming the model-update Job from `ollama-model-update-qwen35` to `ollama-model-update-gemma4` caused Flux to fail reconciliation for the entire kustomization. K8s Jobs are immutable once created. Fix: `kubectl delete job ollama-model-update-gemma4 -n ollama`, then reconcile.
104+
105+
**Lesson**: When renaming Jobs in GitOps, delete the old Job before pushing the manifest change.
106+
100107
## Future Work
101108

102109
- Consider gemma4:e4b if a GPU upgrade happens

docs/runbooks/voice-pe-firmware-upgrade.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,8 +342,16 @@ This announces + explicitly turns off the LED ring.
342342
343343
| Date | From | To | Reason | Issues Hit |
344344
|------|------|----|--------|------------|
345+
| 2026-04-05 | 25.12.4 | 26.4.0 | TTS stutter/timeout fix (media playback stability) | ESPHome Docker image tag mismatch (2026.4.0 doesn't exist, use 2026.3.2) |
345346
| 2026-02-24 | 25.11.0 | 25.12.4 | Fix LED stuck-on bug (#382) | SOPS decrypt, missing manual_ip, no serial logs |
346347
348+
## Lessons Learned (2026-04-05 Upgrade)
349+
350+
1. **Voice PE firmware version ≠ ESPHome Docker image version.** Voice PE firmware `26.4.0` compiles with ESPHome `2026.3.2`. The Docker tag `2026.4.0` doesn't exist.
351+
2. **TTS stutter was firmware-side, not server-side.** Piper synthesis is 21ms, audio fetch via proxy is 19ms. The stutter was caused by small audio buffers in firmware 25.12.4. Firmware 26.4.0 adds `buffer_size: 250000` for announcements.
352+
3. **Remaining stutter is network topology.** Voice PE (192.168.86.x) fetches TTS audio via socat proxy through 3 network hops. HAOS is reachable at 192.168.86.22 but changing `internal_url` would break other integrations.
353+
4. **`power_save_mode: none` is critical and NOT in upstream.** Always verify it persists across upgrades.
354+
347355
## Lessons Learned (2026-02-24 Upgrade)
348356
349357
1. **SOPS secrets must be decrypted before Docker compile** — Docker can't access the age key. The compile will succeed but bake in the encrypted blob as the SSID, which is obviously wrong.

0 commit comments

Comments
 (0)