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
  • Config: /home/levander/nvr/frigate/config.yml (mounted as /confignot /srv/frigate/config, same as 2026-08-22-frigate-recording-retention-config-not-jam)
  • 4 cameras telep_cam1telep_cam4 on two Reolink-style dual-lens units at 192.168.30.119 and 192.168.30.139, restreamed through the internal go2rtc
  • Changes made: birdseye.mode: continuous → objects, birdseye.width: 3840 → 1920, face_recognition.enabled: true → false
  • Measured: VRAM −344 MiB, container RAM −723 MiB. CPU: INCONCLUSIVE
  • Backup: /home/levander/nvr/frigate/config.yml.bak-<timestamp>

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 apart
top -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.

The cost showed up as a permanent process:

ffmpeg -f rawvideo -video_size 3840x1080 ... -codec:v mpeg1video

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:

CheckResult
/media/frigate/clips/faces/EMPTY — directory created Jul 15, never populated
GET /api/faces{}
Last 500 events with a sub_label0

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)

MetricBeforeAfterΔ
Frigate GPU VRAM2148 MiB1804 MiB−344 MiB
embeddings_manager VRAM678 MiB324 MiB−354 MiB
Frigate container RAM3.887 GiB3.164 GiB−723 MiB
Birdseye canvas3840×10801920×1080half the pixels
CPU149.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.
  • Detectoronnx / yolo-generic at 640×640: ~11–19% CPU, 394 MiB VRAM. Normal.
  • 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

  1. /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 —
    tmpfs:
      - /tmp/cache:size=1g
    (/tmp/cache is the same directory used as the jam diagnostic in 2026-08-22-frigate-recording-retention-config-not-jam and 2026-08-06-frigate-detect-record-jam-cpu-starvation.)
  2. 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:

/home/levander/nvr/frigate/config.yml.bak-<timestamp>

Verification performed

  • Config YAML validated inside the container before restarting.
  • docker restart frigatehealthy 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.