Investigated why Frigate 0.17.2 looked like it was eating telep-mainframe alive. Two real wins found (birdseye encoding a 3840×1080 canvas 24/7 and face_recognition enabled with zero faces ever enrolled) — plus one important measurement trap: the alarming number was a ps %CPU lifetime average taken ~12 minutes after a reboot, so it was mostly post-boot startup churn. Memory/VRAM wins are solid; the CPU win is unproven — do not claim one. See also 2026-09-02-session-handover.
For Agents — quick facts
Container frigate, image ghcr.io/blakeblackshear/frigate:stable-tensorrt, version 0.17.2
Finding 0 — the measurement trap (read this first)
The whole investigation started from a scary number: Frigate at 149.81% CPU. That number was largely an artifact.
ps -eo pcpu is a LIFETIME AVERAGE, not instantaneous CPU
ps reports CPU as total CPU time ÷ process lifetime since start. Every process on the box was only ~12 minutes old on a fresh boot, so all the post-reboot startup churn was permanently baked into the average. Load average was visibly still settling the whole time: 15.93 → 6.04 → 3.86.
Correct tools on a freshly-booted box:
docker stats --no-stream # run several times, a few seconds aparttop -bn2 # the SECOND iteration is the real one
Never conclude from a single ps %CPU reading. And for scale: 149.81% on a 24-core box is ~6.2% of total capacity — noticeable, not pathological.
Finding 1 — birdseye was set to continuous at 3840×1080
birdseye: enabled: true restream: true mode: continuous # encodes 24/7 even with zero viewers width: 3840 # Frigate default is 1280x720 height: 1080
mode: continuous means the birdseye canvas is composited and encoded all the time, whether or not anyone is watching. At 3840×1080 that is a 4.1-megapixel canvas.
That is the jsmpeg live-view encoder doing a software mpeg1 encode of 4.1 MP, continuously — the single largest ffmpeg CPU consumer in the stack.
Change applied:mode: objects + width: 1920 → canvas is now 2.1 MP, half the pixels. Verified after restart: the 3840x1080 encoder is gone, replaced by 1920x1080.
GOTCHA — restream: true keeps the birdseye encoder alive even under mode: objects
With restream: true, go2rtc holds a consumer attached to the birdseye stream, so the encoder appears to keep running regardless of mode. mode: objects only shrinks what is composited, not whether it encodes.
To make birdseye genuinely idle when nothing is detected, also set restream: false. OPEN ITEM — not done.
Before flipping it: check who still consumes the birdseye restream. The TV camwall was moved off birdseye onto an explicit 4-substream composite back in 2026-07-28-camwall-4-substream-composite, and the NVENC-corruption note 2026-07-18-birdseye-nvenc-blue-glare predates that move — so the historical consumer may already be gone, but verify before disabling.
Finding 2 — face_recognition enabled, zero faces ever enrolled
face_recognition: enabled: true model_size: large
It had been loaded and burning 678 MiB of VRAM since July while being structurally incapable of recognising anyone. Evidence it was pure dead weight:
Check
Result
/media/frigate/clips/faces/
EMPTY — directory created Jul 15, never populated
GET /api/faces
{}
Last 500 events with a sub_label
0
Change applied:enabled: false.
Disabling face_recognition does NOT remove frigate.embeddings_manager
In Frigate 0.17 the embeddings_manager process also serves semantic search and LPR — face recognition is only one of its consumers. After the change it dropped 678 MiB → 324 MiB (halved); it did not disappear. If you expected the process to vanish and it didn’t, that is correct behaviour, not a failed config change.
Measured results (before → after)
Metric
Before
After
Δ
Frigate GPU VRAM
2148 MiB
1804 MiB
−344 MiB
embeddings_manager VRAM
678 MiB
324 MiB
−354 MiB
Frigate container RAM
3.887 GiB
3.164 GiB
−723 MiB
Birdseye canvas
3840×1080
1920×1080
half the pixels
CPU
149.81% (bad baseline)
37.94% / 30.40% / 131.08%
INCONCLUSIVE
Do NOT claim a CPU win from this change
Post-change samples were highly bursty (37.94%, 30.40%, 131.08%) and the 149.81% “before” was measured during post-boot startup, so it is not a fair baseline. There is no valid before/after CPU comparison here. The memory and VRAM wins are solid and measured; the CPU story is unresolved.
Already well configured — do NOT “optimize” these
Re-tuning any of these would be churn with no payoff:
Detection streams — 1280×720 @ 5 fps with preset-nvidia hwaccel + scale_cuda: only ~2.7% CPU per camera.
Recording ffmpegs — use -c:v copy (no transcode): ~1.5% CPU each.
The main/sub stream split those numbers depend on (detect on camN_sub 720p, record on camN_main 2304×1296) was established in car filter, HD recording + HD upgrade.
Remaining untouched levers
/tmp/cache is an ANONYMOUS DOCKER VOLUME, not tmpfs.
Frigate writes 10-second segments from all 4 cameras there before muxing — it is designed to be RAM-backed. Right now it is churning /var/lib/docker on disk.
Fix: add to the compose service —
record.motion.days: 10 retains ALL motion segments, on top of the separate 14-day alerts + detections retention. /srv/frigate/recordings is at 496 GB (disk 735 G / 3.6 T used, 22%). Not urgent, but it is the biggest storage lever. ⚠️ That motion.days: 10 was added deliberately in 2026-08-22-frigate-recording-retention-config-not-jam to fix genuinely-missing overnight footage — do not just delete it or you re-open that bug.
Rollback
A timestamped backup was written alongside the original before editing:
Config YAML validated inside the container before restarting.
docker restart frigate → healthy in 20 s.
No config errors in the new logs.
Confirmed the 3840x1080 rawvideo/mpeg1 encoder is gone and a 1920x1080 one took its place.
docker logs frigate REPLAYS FULL HISTORY — check timestamps before blaming a restart
Grepping the logs for errors after the restart surfaced entries dated 2026-07-27. They were not from this restart. docker logs has no implicit “since last start” — always pass --since or read the timestamps before concluding a change broke something.