Pulled from /mnt/d/frigate/config/config.yml with the camera logins and addresses replaced by placeholders; otherwise as it runs. Section notes follow the file. To add a camera to an existing Frigate, skip to the copy-this template. The machine underneath is the WSL2 setup files, and the detectors block at the top has a page of its own, the OpenVINO ZMQ detector.
mqtt:
enabled: false
detectors:
ov_gpu:
type: zmq
endpoint: tcp://172.17.0.1:5555
request_timeout_ms: 1000
model:
path: /openvino-model/ssdlite_mobilenet_v2.xml
width: 300
height: 300
input_tensor: nhwc
input_pixel_format: bgr
labelmap_path: /openvino-model/coco_91cl_bkgr.txt
record:
enabled: true
continuous:
days: 3
alerts:
retain:
days: 21
detections:
retain:
days: 21
snapshots:
enabled: true
retain:
default: 21
objects:
track:
- person
- car
- cat
- dog
filters:
cat:
threshold: 0.6
min_score: 0.4
motion:
threshold: 20
contour_area: 8
improve_contrast: true
audio:
enabled: false
min_volume: 500
listen:
- meow
- cat
- caterwaul
go2rtc:
streams:
cat_water_bowl:
- rtsp://<CAMERA_USER>:<CAMERA_PASSWORD>@192.168.1.<N>:554/stream1
cat_water_bowl_sub:
- rtsp://<CAMERA_USER>:<CAMERA_PASSWORD>@192.168.1.<N>:554/stream2
cat_food_bowl:
- rtsp://<CAMERA_USER>:<CAMERA_PASSWORD>@192.168.1.<N>:554/stream1
cat_food_bowl_sub:
- rtsp://<CAMERA_USER>:<CAMERA_PASSWORD>@192.168.1.<N>:554/stream2
cat_treat_dispenser:
- rtsp://<CAMERA_USER>:<CAMERA_PASSWORD>@192.168.1.<N>:554/stream1
cat_treat_dispenser_sub:
- rtsp://<CAMERA_USER>:<CAMERA_PASSWORD>@192.168.1.<N>:554/stream2
cat_scratching_pad:
- rtsp://<CAMERA_USER>:<CAMERA_PASSWORD>@192.168.1.<N>:554/stream1
cat_scratching_pad_sub:
- rtsp://<CAMERA_USER>:<CAMERA_PASSWORD>@192.168.1.<N>:554/stream2
helen_food_face:
- rtsp://<CAMERA_USER>:<CAMERA_PASSWORD>@192.168.1.<N>:554/stream1
helen_food_face_sub:
- rtsp://<CAMERA_USER>:<CAMERA_PASSWORD>@192.168.1.<N>:554/stream2
playroom_cam:
- rtsp://<CAMERA_USER>:<CAMERA_PASSWORD>@192.168.1.<N>:554/stream1
playroom_cam_sub:
- rtsp://<CAMERA_USER>:<CAMERA_PASSWORD>@192.168.1.<N>:554/stream2
bed_cam:
- rtsp://<CAMERA_USER>:<CAMERA_PASSWORD>@192.168.1.<N>:554/stream1
bed_cam_sub:
- rtsp://<CAMERA_USER>:<CAMERA_PASSWORD>@192.168.1.<N>:554/stream2
bed_cam_2:
- rtsp://<CAMERA_USER>:<CAMERA_PASSWORD>@192.168.1.<N>:554/stream1
bed_cam_2_sub:
- rtsp://<CAMERA_USER>:<CAMERA_PASSWORD>@192.168.1.<N>:554/stream2
cameras:
cat_water_bowl:
ffmpeg:
inputs:
- path: rtsp://127.0.0.1:8554/cat_water_bowl_sub
roles:
- detect
- path: rtsp://127.0.0.1:8554/cat_water_bowl
roles:
- record
detect:
width: 640
height: 360
fps: 2
enabled: true
audio:
enabled: false
objects:
track:
- cat
- person
cat_food_bowl:
ffmpeg:
inputs:
- path: rtsp://127.0.0.1:8554/cat_food_bowl_sub
roles:
- detect
- path: rtsp://127.0.0.1:8554/cat_food_bowl
roles:
- record
detect:
width: 640
height: 360
fps: 2
enabled: true
audio:
enabled: false
objects:
track:
- cat
- person
cat_treat_dispenser:
ffmpeg:
inputs:
- path: rtsp://127.0.0.1:8554/cat_treat_dispenser_sub
roles:
- detect
- path: rtsp://127.0.0.1:8554/cat_treat_dispenser
roles:
- record
detect:
width: 640
height: 360
fps: 2
enabled: true
audio:
enabled: false
objects:
track:
- cat
- person
cat_scratching_pad:
ffmpeg:
inputs:
- path: rtsp://127.0.0.1:8554/cat_scratching_pad_sub
roles:
- detect
- path: rtsp://127.0.0.1:8554/cat_scratching_pad
roles:
- record
detect:
width: 640
height: 360
fps: 2
enabled: true
audio:
enabled: false
objects:
track:
- cat
- person
helen_food_face:
ffmpeg:
inputs:
- path: rtsp://127.0.0.1:8554/helen_food_face_sub
roles:
- detect
- path: rtsp://127.0.0.1:8554/helen_food_face
roles:
- record
detect:
width: 640
height: 360
fps: 2
enabled: true
audio:
enabled: false
objects:
track:
- cat
- person
playroom_cam:
ffmpeg:
inputs:
- path: rtsp://127.0.0.1:8554/playroom_cam_sub
roles:
- detect
- path: rtsp://127.0.0.1:8554/playroom_cam
roles:
- record
detect:
width: 640
height: 360
fps: 2
enabled: true
audio:
enabled: false
objects:
track:
- cat
- person
bed_cam:
ffmpeg:
inputs:
- path: rtsp://127.0.0.1:8554/bed_cam_sub
roles:
- detect
- path: rtsp://127.0.0.1:8554/bed_cam
roles:
- record
detect:
width: 640
height: 360
fps: 2
enabled: true
audio:
enabled: false
objects:
track:
- cat
- person
bed_cam_2: # audio trial camera; the whole camera is switched off
enabled: false
ffmpeg:
inputs:
- path: rtsp://127.0.0.1:8554/bed_cam_2_sub
roles:
- detect
- path: rtsp://127.0.0.1:8554/bed_cam_2
roles:
- record
- audio
detect:
width: 640
height: 360
fps: 2
enabled: true
audio:
enabled: true
objects:
track:
- cat
# ---- house cameras: never published, detect off, 21 days continuous ----
front_door:
ffmpeg:
inputs:
- path: rtsp://<CAMERA_USER>:<CAMERA_PASSWORD>@192.168.1.<N>:554/stream2
roles:
- detect
- path: rtsp://<CAMERA_USER>:<CAMERA_PASSWORD>@192.168.1.<N>:554/stream1
roles:
- record
detect:
enabled: false
width: 640
height: 360
fps: 3
record:
enabled: true
continuous:
days: 21
back_yard: # non-Tapo camera: its own path scheme
ffmpeg:
inputs:
- path: rtsp://<CAMERA_USER>:<CAMERA_PASSWORD>@192.168.1.<N>/live/ch1
roles:
- detect
- path: rtsp://<CAMERA_USER>:<CAMERA_PASSWORD>@192.168.1.<N>/live/ch0
roles:
- record
detect:
enabled: false
width: 640
height: 360
fps: 3
record:
enabled: true
continuous:
days: 21
carport:
ffmpeg:
inputs:
- path: rtsp://<CAMERA_USER>:<CAMERA_PASSWORD>@192.168.1.<N>:554/stream2
roles:
- detect
- path: rtsp://<CAMERA_USER>:<CAMERA_PASSWORD>@192.168.1.<N>:554/stream1
roles:
- record
detect:
enabled: false
width: 640
height: 360
fps: 3
record:
enabled: true
continuous:
days: 21
version: 0.17-0
mqtt
Off. Nothing on this box subscribes to Frigate's MQTT topics; the Shorts pipeline polls /api/events over HTTP every fifteen minutes and Home Assistant's job here is the treat relay, not Frigate. Turning MQTT off removes a broker from the list of things that can be down.
detectors
One detector, named ov_gpu, of type zmq. Frigate does not load a model at all; it sends each detect frame to tcp://172.17.0.1:5555 and reads the detections back. 172.17.0.1 is docker's default bridge gateway, which is the WSL host, where a small Python process holds the Intel UHD 630 through OpenVINO. That process, its systemd unit, and the protocol are on the detector page. request_timeout_ms: 1000 is roughly eighty times the steady-state round trip of 12.5 ms; it only fires if the server is gone.
model
Even with an external detector Frigate needs to know the model's input contract, because Frigate does the resize and layout before it sends the frame. This is the bundled SSDLite MobileNet v2 OpenVINO IR: 300×300, nhwc (batch, height, width, channels), BGR pixel order, and the 91-class COCO labelmap with a background entry so the class ids the detector returns map to the right words. The host process loads its own copy of the same files, so the ids agree.
record
The shape that makes three weeks affordable: continuous: 3 days keeps everything from every recording camera for three days, and alerts / detections at 21 days keep the segments where something tracked was in frame for three weeks. On the cat cameras that means three weeks of cat without three weeks of empty room. The house cameras override this per camera to 21 days continuous, because a front door is a different question. Frigate's own count of what that keeps (/api/recordings/storage, 2026-09-03) is about 420 GiB across the eleven cameras on a 3.7 TB drive; the retention page has the per-camera numbers and does the arithmetic for other camera counts.
snapshots
On, 21 days, matching the event retention. The Shorts pipeline pulls the best-scoring event's snapshot as the hero frame of a visit (the still that goes to the vision model alongside the clip), but it decides whether a visit is worth publishing from the event timings, not from the snapshot. The public site's "latest" stills come from a different endpoint entirely, so these are for review, not display.
objects
Globally: person, car, cat, dog. Each cat camera narrows that to cat and person; car and dog only matter if a house camera ever turns detection on. The cat filter is the one tuned number in the file: threshold: 0.6 is the score a tracked object must reach at some point to become an event, min_score: 0.4 is the floor below which a single frame's detection is ignored entirely. SSDLite is confident about a cat at a bowl and less confident about a cat's back walking away; 0.6 / 0.4 keeps both. If your cushions start becoming cats, raise min_score before threshold.
person stays on the cat cameras because the Shorts pipeline has a deny gate: a visit with a person in any frame is never published. That gate can only refuse what Frigate detected, so the cat cameras must look for people even though the show is never about people.
motion
threshold: 20 and contour_area: 8 are a little more sensitive than Frigate's defaults, because a cat drinking is a small, slow motion and the cameras are close. improve_contrast: true helps the dark-room cameras at night. Motion is the gate in front of the detector: a frame with no motion regions is never sent over ZMQ, which is why detection_fps in the stats is usually well below camera_fps.
audio
Configured, disabled. The Tapo streams carry PCMA/8000 audio, and Frigate can listen for meow, cat and caterwaul as audio events. We trialled it on bed_cam_2 (the one camera with an audio role in its inputs) and left it off globally; the trial is parked, not concluded. It stays in the file so it can be resumed by flipping one boolean.
go2rtc.streams
Sixteen entries for eight cat camera blocks (seven running, plus the switched-off bed_cam_2 trial): <name> pulls /stream1 (the full-resolution main stream), <name>_sub pulls /stream2 (640×360). go2rtc connects to each camera once and republishes on rtsp://127.0.0.1:8554/<name>. Frigate's own processes, the two 24/7 YouTube pushes, and anything else on the box all read from go2rtc, never from the camera. The three house cameras have no go2rtc entry; nothing but Frigate is meant to see them.
cameras — the cat-cam block
Seven cameras share one block, differing only in the name. Sub-stream for detect, main stream for record. Detect at 640×360 — the sub-stream's native size, so no scaling — at 2 fps. A cat at a bowl stays for many seconds; 2 fps loses nothing and keeps seven cameras' worth of detection at about 14 fps total, which the iGPU answers at about 20 % busy. audio: enabled: false per camera is explicit so the global block can be flipped without every camera following.
bed_cam_2 is the audio-trial camera, switched off at the camera level with enabled: false; its detect.enabled is still true, which is why /api/stats reports detection_enabled: true for it. It shows up in the stats with camera_fps 0 and pid: null, because Frigate never starts a process for a disabled camera. That is what a disabled camera looks like, as opposed to a broken one (back_yard below: process alive, camera_fps 0.0).
cameras — the house block
Front door, back yard, carport. Three differences from the cat block: the inputs point at the camera directly (no go2rtc entry to restream), detect.enabled: false so nothing is tracked and nothing becomes an event, and a per-camera record.continuous.days: 21 that overrides the global 3. They run at 3 fps instead of 2; with detection off the only cost is decode, and a door is the one place a third frame a second is worth having. back_yard is not a Tapo; its RTSP paths are /live/ch0 for the main and /live/ch1 for the sub, which is a reminder that every maker has its own scheme and Frigate's camera docs are where to look them up.
Copy this for the next camera
Replace <JOB> in five places, the address once, and paste the two fragments into the matching sections.
If the next camera is not bought yet: everything on this page was written against Tapo C120 and C210 units. The link that follows is an affiliate link to TP-Link's US store, meaning a purchase through it pays this site a small commission at no extra cost to you; it changes nothing above: Tapo C120, the one we bought last.
# 1. go2rtc: pull each stream from the camera once
go2rtc:
streams:
cat_<JOB>:
- rtsp://<CAMERA_USER>:<CAMERA_PASSWORD>@192.168.1.<N>:554/stream1
cat_<JOB>_sub:
- rtsp://<CAMERA_USER>:<CAMERA_PASSWORD>@192.168.1.<N>:554/stream2
# 2. the camera: detect on the sub, record the main
cameras:
cat_<JOB>:
ffmpeg:
inputs:
- path: rtsp://127.0.0.1:8554/cat_<JOB>_sub
roles:
- detect
- path: rtsp://127.0.0.1:8554/cat_<JOB>
roles:
- record
detect:
width: 640
height: 360
fps: 2
enabled: true
audio:
enabled: false
objects:
track:
- cat
- person
What this config produces
$ curl -s http://127.0.0.1:5000/api/stats
{
"cameras": {
"cat_water_bowl": {"camera_fps": 2.1, "process_fps": 2.1, "skipped_fps": 0.0, "detection_fps": 1.3, "detection_enabled": true, "pid": 1143},
"playroom_cam": {"camera_fps": 2.1, "process_fps": 2.1, "skipped_fps": 0.0, "detection_fps": 2.1, "detection_enabled": true, "pid": 1266},
"cat_food_bowl": {"camera_fps": 2.1, "process_fps": 2.1, "skipped_fps": 0.0, "detection_fps": 4.3, "detection_enabled": true, "pid": 1392},
"cat_treat_dispenser":{"camera_fps": 2.1, "process_fps": 2.1, "skipped_fps": 0.0, "detection_fps": 2.5, "detection_enabled": true, "pid": 1515},
"front_door": {"camera_fps": 3.1, "process_fps": 3.1, "skipped_fps": 0.0, "detection_fps": 0.0, "detection_enabled": false, "pid": 1638},
"back_yard": {"camera_fps": 0.0, "process_fps": 0, "skipped_fps": 0.0, "detection_fps": 0, "detection_enabled": false, "pid": 1762},
"carport": {"camera_fps": 3.0, "process_fps": 3.0, "skipped_fps": 0.0, "detection_fps": 0.0, "detection_enabled": false, "pid": 1885},
"cat_scratching_pad": {"camera_fps": 2.1, "process_fps": 2.1, "skipped_fps": 0.0, "detection_fps": 3.4, "detection_enabled": true, "pid": 2033},
"helen_food_face": {"camera_fps": 2.1, "process_fps": 2.0, "skipped_fps": 0.0, "detection_fps": 3.1, "detection_enabled": true, "pid": 2197},
"bed_cam_2": {"camera_fps": 0, "process_fps": 0, "skipped_fps": 0, "detection_fps": 0, "detection_enabled": true, "pid": null},
"bed_cam": {"camera_fps": 2.0, "process_fps": 2.0, "skipped_fps": 0.0, "detection_fps": 0.0, "detection_enabled": true, "pid": 2337}
},
"detectors": {"ov_gpu": {"inference_speed": 12.57, "detection_start": 0.0, "pid": 1073}},
"camera_fps": 20.7, "process_fps": 20.6, "skipped_fps": 0.0, "detection_fps": 16.7,
"service": {
"uptime": 337595, "version": "0.17.2-3d4dd3a",
"storage": {
"/media/frigate/recordings": {"total": 3815430.0, "used": 905101.5, "free": 2910328.5, "mount_type": "9p"},
"/media/frigate/clips": {"total": 3815430.0, "used": 905101.5, "free": 2910328.5, "mount_type": "9p"},
"/tmp/cache": {"total": 953.7, "used": 83.2, "free": 870.4, "mount_type": "tmpfs"},
"/dev/shm": {"total": 256.0, "used": 76.1, "free": 179.9, "mount_type": "tmpfs", "available": 206.0, "camera_frame_size": 9.2, "shm_frame_count": 22, "min_shm": 234}
},
"last_updated": 1788394640
}
}
That capture is 3.9 days into the container's uptime: 20.7 camera-frames a second in, zero skipped, 16.7 detector calls a second at 12.57 ms each. detection_fps counts inference calls, not frames; a frame with several regions of interest costs several calls, which is how cat_food_bowl reads 4.3 on a 2.1 fps feed. back_yard at 0.0 with a live pid is a camera that was offline (it had been for days); bed_cam_2 at 0 with pid: null is the disabled one. service.storage is the filesystem's view of the whole D: drive through 9p, 905 GB used of 3.8 TB, which is the drive and not the recordings; the retention page has Frigate's own per-camera count.