Skip to content

Frigate NVR Setup Guide

Dahua IPC-T54IR-AS-S3 + Intel iGPU + Home Assistant

Stack: Debian 13 · Docker · Mosquitto on a separate host (see Home Assistant)
Cameras: 3x Dahua/EmpireTech IPC-T54IR-AS-S3 4MP turret, H.265, PoE
Image: ghcr.io/blakeblackshear/frigate:0.18.2 (default image, includes OpenVINO)
Hardware: Intel Core i5-8500T with UHD Graphics 630, recordings on a dedicated 3 TB WD Red

Frigate now runs on the Intel iGPU: VAAPI for video decode and OpenVINO for object detection, both on the same UHD 630. It started out on an NVIDIA GTX 1080 with the -tensorrt image; that setup is kept below as an alternative in the NVIDIA (TensorRT) tabs. A 6th-gen-or-newer Intel iGPU handles one to three cameras comfortably, uses a fraction of the power of a discrete card, and needs no extra drivers on the host.

Hard-won lessons (masks breaking after a model change, parked cars alerting, debugging through the API) are in Lessons learned: smart home.


1. Physical Setup

HDD for Recordings

Recordings go on their own disk, not the main storage pool. Frigate writes 24/7, which would keep pool disks spinning, churn SnapRAID parity (every new segment is a change to sync), and compete with media I/O. A dedicated drive also means the NVR keeps recording while the pool is busy with maintenance. Frigate's database lives on the same disk (database.path below), so recordings and their index move together.

This wipes the disk

fdisk and mkfs.ext4 destroy everything on the target disk. Double-check /dev/sdX with lsblk and back up anything you need first.

lsblk                          # identify new disk
sudo fdisk /dev/sdX            # g (GPT), n (partition), accept defaults, w (write)
sudo mkfs.ext4 /dev/sdX1
sudo mkdir -p /mnt/frigate
sudo blkid /dev/sdX1           # copy UUID
echo "UUID=<uuid>  /mnt/frigate  ext4  defaults,noatime,nofail  0  2" | sudo tee -a /etc/fstab
sudo systemctl daemon-reload
sudo mount -a
df -h /mnt/frigate             # verify

nofail lets the host boot if the drive is absent (Frigate then fails loudly instead of the whole server sitting in emergency mode). noatime skips a metadata write every time a segment is read for playback. Keep an eye on the drive with SMART monitoring: a surveillance disk works harder than any other disk in the house.

Camera Default IP

Warning

Dahua cameras ship with a static IP of 192.168.1.108 -- they do not DHCP by default. To access them initially (If on different VLAN):

  1. Set laptop NIC to 192.168.1.50 / 255.255.255.0
  2. Browse to http://192.168.1.108
  3. Set admin password (required on first login)
  4. Go to Setup > Network > TCP/IP, assign static IoT VLAN IP
  5. Confirm RTSP enabled: Setup > Network > Port, default port 554

Assign cameras sequential statics in <IOT_SUBNET> (e.g. .101, .102, .103)

PoE Switch (SG2016P)

The SG2016P has 8 PoE+ ports (802.3at/af, 30W per port, 120W total budget). PoE ports are ports 1-8 -- confirm in Omada UI under Devices > [switch] > PoE. Each camera draws ~12-15W, well within budget for 3 cameras.


2. Camera Configuration

Stream Settings (Camera Web UI)

Main Stream (record -- full quality): - Resolution: 2688x1520 - Framerate: 20fps - Bitrate: 4096-6144 kbps - Codec: H.265 - I-frame interval: 40 (2x framerate)

Sub Stream 2 (detect -- use subtype=2, not subtype=1): - Resolution: 1280x720 - Framerate: 10fps - Bitrate: 1024 kbps - Codec: H.265 - I-frame interval: 20 (2x framerate)

Why Sub Stream 2:

Sub Stream 1 is hard-capped at 704x480 by Dahua firmware -- this is by design for legacy NVR compatibility. Sub Stream 2 allows up to 1280x720 and is referenced with subtype=2 in the RTSP URL.

Why not max resolution on sub stream:

Detection runs on every frame of the sub stream. Frigate crops regions of motion and scales them to the model's input size (320x320 on the iGPU, 640x640 on the NVIDIA build) regardless -- higher sub stream resolution adds decode cost with little detection gain. The main stream captures full resolution for recordings.

RTSP URL Format

Main:  rtsp://admin:<YOUR_PASSWORD>@<CAMERA_IP>:554/cam/realmonitor?channel=1&subtype=0
Sub2:  rtsp://admin:<YOUR_PASSWORD>@<CAMERA_IP>:554/cam/realmonitor?channel=1&subtype=2

Password note: Special characters (#, $, @) in passwords break RTSP URL parsing. Use alphanumeric-only passwords on the cameras (or URL-encode them, e.g. @ → %40).

Verify Streams Before Frigate

ffprobe -v quiet -print_format json -show_streams \
  'rtsp://admin:<YOUR_PASSWORD>@<CAMERA_IP>:554/cam/realmonitor?channel=1&subtype=0'

Confirms codec, resolution, framerate. Run for all three cameras on both subtype=0 and subtype=2.


3. Host Prerequisites

Nothing to install: the i915 kernel driver is part of Debian's kernel, and the Frigate image ships the VAAPI and OpenVINO user-space drivers. Just check the render node exists:

ls -l /dev/dri/
# card0  renderD128   <- renderD128 is what Frigate needs

Optional, for watching GPU load from the host:

sudo apt install intel-gpu-tools
sudo intel_gpu_top

OpenVINO's GPU device needs a 6th-gen (Skylake) or newer Intel iGPU. The UHD 630 is gen 9.5, which is covered by preset-vaapi. Gen 13+ and Arc GPUs use the preset-intel-qsv-* presets instead.

NVIDIA Driver + Container Toolkit (Debian 13)

Enable the contrib non-free non-free-firmware components in /etc/apt/sources.list.d/debian.sources, then:

sudo apt update
sudo apt install linux-headers-amd64 nvidia-kernel-dkms nvidia-driver libnvcuvid1
sudo reboot

# Verify driver and the video decode library
nvidia-smi
ldconfig -p | grep nvcuvid

# Install Container Toolkit
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | \
  sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
  && curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
  sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
  sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list

sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit

# Configure Docker runtime
# WARNING: nvidia-ctk writes to /etc/docker/daemon.json
# Do NOT set default-runtime to nvidia -- use deploy.resources in compose instead
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker

# Verify
docker run --rm --gpus all debian:trixie-slim nvidia-smi

daemon.json warning: nvidia-ctk creates or edits /etc/docker/daemon.json. If Docker breaks after running it, check the file -- remove it entirely if it was empty before (sudo rm /etc/docker/daemon.json && sudo systemctl restart docker). Use deploy.resources in compose instead of setting a default runtime.

NVDEC library: ffmpeg's NVIDIA hardware decode needs libnvcuvid.so.1. Debian's nvidia-driver package does not pull it in, so install libnvcuvid1 explicitly (on Ubuntu the same trap is the -server driver variant, which omits it). Debian 13's packaged driver is the 550 series; very new cards may need NVIDIA's own repo.


4. ONNX Detection Model

Frigate 0.16+ does not ship pre-built YOLO models. You build one once and drop it into config/model_cache/. Both setups use YOLOv9 exported to ONNX, at different sizes.

YOLOv9-s at 320x320. Frigate's docs list YOLOv9 as the recommended model for OpenVINO on a GPU, and s at 320 as the starting point. RF-DETR needs an Xe iGPU or Arc card, so it isn't an option on a gen 9.5 UHD 630.

Why not the default SSDLite MobileNet v2 that OpenVINO ships with? It ran here first and was the root cause of a false-alert problem: under night IR it produced jittery detections that kept dying and respawning (each one a new alert) and it confidently labelled a parked camper as a person and as a car. YOLOv9-s (~46 mAP on COCO vs ~22) fixed that. On this iGPU it takes about 20 ms per inference against 8 ms for SSDLite, which is plenty of headroom at 5 fps detection.

mkdir -p /mnt/appdata/frigate/config/model_cache
cd /mnt/appdata/frigate/config/model_cache
docker build . --build-arg MODEL_SIZE=s --build-arg IMG_SIZE=320 --output . -f- <<'EOF'
FROM python:3.11 AS build
RUN apt-get update && apt-get install --no-install-recommends -y cmake libgl1 && rm -rf /var/lib/apt/lists/*
COPY --from=ghcr.io/astral-sh/uv:0.10.4 /uv /bin/
WORKDIR /yolov9
ADD https://github.com/WongKinYiu/yolov9.git .
RUN uv pip install --system -r requirements.txt
RUN uv pip install --system onnx==1.18.0 onnxruntime onnx-simplifier==0.4.* onnxscript
ARG MODEL_SIZE
ARG IMG_SIZE
ADD https://github.com/WongKinYiu/yolov9/releases/download/v0.1/yolov9-${MODEL_SIZE}-converted.pt yolov9-${MODEL_SIZE}.pt
RUN sed -i "s/ckpt = torch.load(attempt_download(w), map_location='cpu')/ckpt = torch.load(attempt_download(w), map_location='cpu', weights_only=False)/g" models/experimental.py
RUN python3 export.py --weights ./yolov9-${MODEL_SIZE}.pt --imgsz ${IMG_SIZE} --simplify --include onnx
FROM scratch
ARG MODEL_SIZE
ARG IMG_SIZE
COPY --from=build /yolov9/yolov9-${MODEL_SIZE}.onnx /yolov9-${MODEL_SIZE}-${IMG_SIZE}.onnx
EOF

Output: yolov9-s-320.onnx. It has one input (images, [1,3,320,320] float32) and one output ([1,84,2100]: 4 box coordinates + 80 COCO classes), which is the layout model_type: yolo-generic expects.

YOLOv9-m at 640x640. Same export, bigger model:

cd /path/to/appdata/frigate/config/model_cache
# same docker build command as the Intel tab, with:
docker build . --build-arg MODEL_SIZE=m --build-arg IMG_SIZE=640 --output . -f- <<'EOF'
...
EOF

Output: yolov9-m-640.onnx

Why 640px: The sub stream is 1280x720. Building at 320px throws away resolution you paid for -- 640px gives meaningfully better detection accuracy with negligible GPU overhead on a 1080.

Model variants: t (tiny, 4.4MB) → s (small, 14MB) → m (medium, 38MB) → c (large, 49MB) → e (112MB). For 3 cameras on a GTX 1080, -m at 640px is the right balance -- inference ~15-25ms, meaningfully better accuracy than -s especially for IR/night footage.

onnx-simplifier pinned to 0.4.x: Unpinned (>=0.4.1) resolves to 0.5.0 which pulls onnxsim==0.6.2 which fails to build from source. Frigate's own export snippet pins 0.4.* for the same reason.

Labelmap changes with the model

YOLOv9 uses the 80-class COCO labelmap (/labelmap/coco-80.txt), not the 91-class one SSDLite uses. Every common label (person, car, dog, cat, bird, bicycle, motorcycle, bear) exists in both, but deer does not exist in COCO at all, so don't bother tracking it. And re-check your masks after any model change: see masks stop working after you change the detector model.


5. Docker Compose

compose.yaml
services:
  frigate:
    container_name: frigate
    # Default image includes OpenVINO (Intel) support for detection + enrichments.
    image: ghcr.io/blakeblackshear/frigate:0.18.2
    restart: unless-stopped
    stop_grace_period: 30s
    # Frigate recommends more shared memory for multiple high-res cameras;
    # 1gb is comfortable for 3x 4MP cameras and prevents dropped frames.
    shm_size: "1gb"
    env_file:
      - .env
    environment:
      - TZ=<TZ>
    dns:
      - <YOUR_DNS_SERVER_IP>
    devices:
      - /dev/dri/renderD128:/dev/dri/renderD128 # Intel iGPU - VAAPI decode + OpenVINO GPU inference
    volumes:
      - /mnt/appdata/frigate/config:/config
      - /mnt/frigate:/media/frigate
      # 1GB tmpfs for the recording segment cache: saves disk wear from 24/7 video caching.
      - type: tmpfs
        target: /tmp/cache
        tmpfs:
          size: 1000000000
    ports:
      - "8971:8971"     # Frigate Web UI (authenticated)
      - "8554:8554"     # RTSP restream from go2rtc
      - "8555:8555/tcp" # WebRTC over TCP
      - "8555:8555/udp" # WebRTC over UDP
compose.yaml
services:
  frigate:
    container_name: frigate
    image: ghcr.io/blakeblackshear/frigate:0.18.2-tensorrt
    restart: unless-stopped
    stop_grace_period: 30s
    shm_size: "1gb"
    env_file:
      - .env
    environment:
      - TZ=<TZ>
    dns:
      - <YOUR_DNS_SERVER_IP>
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
    volumes:
      - /path/to/appdata/frigate/config:/config
      - /mnt/frigate:/media/frigate
      - type: tmpfs
        target: /tmp/cache
        tmpfs:
          size: 1000000000
    ports:
      - "8971:8971"
      - "8554:8554"
      - "8555:8555/tcp"
      - "8555:8555/udp"

GPU passthrough: NVIDIA uses deploy.resources.reservations.devices in compose, not devices: mappings. The device mappings (/dev/dri/renderD128) are for Intel/AMD only.

.env.example
FRIGATE_RTSP_PASSWORD=<YOUR_CAMERA_PASSWORD>
FRIGATE_MQTT_PASSWORD=<MQTT_FRIGATE_PASSWORD>
# Recommended: require a login for the go2rtc RTSP restream on :8554
FRIGATE_GO2RTC_RTSP_USERNAME=<RESTREAM_USER>
FRIGATE_GO2RTC_RTSP_PASSWORD=<RESTREAM_PASSWORD>
chmod 600 .env

Frigate substitutes {FRIGATE_*} environment variables into specific config fields (MQTT credentials, camera and go2rtc URLs, go2rtc RTSP credentials), so no password ever has to appear in config.yaml.

Image tag: pin an exact version (0.18.2, or 0.18.2-tensorrt for NVIDIA). stable floats to the newest release, including majors with breaking config changes. Read the release notes and back up /config before every upgrade.

Config lives outside git: /config is on the appdata disk because Frigate edits config.yaml itself when you change masks, zones or settings in the UI. Back it up with the rest of your appdata.

Ports: the RTSP restream (8554) and WebRTC (8555) don't use the UI login. Set go2rtc.rtsp.username/password (shown in the config below) and only expose these ports to trusted networks. Never publish the internal API port 5000, which has no authentication at all.

tmpfs /tmp/cache: Frigate buffers recording segments in memory before writing to disk. Reduces small random writes to the HDD. 1GB is sufficient for 3 cameras.

dns: Required for containers to resolve <YOUR_LOCAL_DOMAIN> hostnames via the local DNS resolver (AdGuard/Unbound chain).


6. Frigate Config

/mnt/appdata/frigate/config/config.yaml. This is the live config, trimmed of comments and sanitized. Pick the detector/ffmpeg block from the tabs; everything else is shared.

config.yaml
version: 0.18-0

tls:
  enabled: false          # HA's Frigate integration and notification proxy can't verify Frigate's self-signed cert

mqtt:
  enabled: true
  host: <MQTT_BROKER_IP>
  port: 1883
  topic_prefix: frigate
  client_id: frigate
  user: frigate
  password: "{FRIGATE_MQTT_PASSWORD}"
  stats_interval: 60

database:
  path: /media/frigate/frigate.db   # on the recordings disk, next to the recordings it indexes

auth:
  enabled: true

go2rtc:
  rtsp:                    # login for clients of the :8554 restream (Frigate itself uses localhost, which is exempt)
    username: "{FRIGATE_GO2RTC_RTSP_USERNAME}"
    password: "{FRIGATE_GO2RTC_RTSP_PASSWORD}"
  streams:
    cam_01:
      - rtsp://admin:{FRIGATE_RTSP_PASSWORD}@<CAMERA_1_IP>:554/cam/realmonitor?channel=1&subtype=0
      - ffmpeg:cam_01#audio=opus       # adds an Opus track so WebRTC live view has audio
    cam_01_sub:
      - rtsp://admin:{FRIGATE_RTSP_PASSWORD}@<CAMERA_1_IP>:554/cam/realmonitor?channel=1&subtype=2
    cam_02:
      - rtsp://admin:{FRIGATE_RTSP_PASSWORD}@<CAMERA_2_IP>:554/cam/realmonitor?channel=1&subtype=0
      - ffmpeg:cam_02#audio=opus
    cam_02_sub:
      - rtsp://admin:{FRIGATE_RTSP_PASSWORD}@<CAMERA_2_IP>:554/cam/realmonitor?channel=1&subtype=2
    cam_03:
      - rtsp://admin:{FRIGATE_RTSP_PASSWORD}@<CAMERA_3_IP>:554/cam/realmonitor?channel=1&subtype=0
      - ffmpeg:cam_03#audio=opus
    cam_03_sub:
      - rtsp://admin:{FRIGATE_RTSP_PASSWORD}@<CAMERA_3_IP>:554/cam/realmonitor?channel=1&subtype=2

detect:
  enabled: true
  fps: 5                 # Frigate's recommended detect rate

objects:
  track: [person, dog, cat, bird, car, motorcycle, bicycle, bear]
  filters:
    person: { min_area: 5000, min_score: 0.5, threshold: 0.7 }
    car:    { min_area: 5000, min_score: 0.5, threshold: 0.7 }
    dog:    { min_score: 0.5, threshold: 0.7 }
    cat:    { min_score: 0.5, threshold: 0.7 }

motion:
  threshold: 30
  contour_area: 10
  improve_contrast: true
  mqtt_off_delay: 30

review:
  alerts:
    labels: [person, bear, car]
  detections:
    labels: [dog, cat, bird, bicycle, motorcycle]

record:
  enabled: true
  continuous:
    days: 7              # everything, 24/7, for a week
  motion:
    days: 14             # then only segments with motion, up to two weeks
  preview:
    quality: medium
  alerts:
    pre_capture: 5
    post_capture: 5
    retain:
      days: 30
      mode: motion
  detections:
    pre_capture: 5
    post_capture: 5
    retain:
      days: 14
      mode: motion

snapshots:
  enabled: true
  timestamp: false
  bounding_box: true
  quality: 70
  retain:
    default: 14
    objects:
      person: 30
      bear: 30

birdseye:
  enabled: true
  mode: objects

telemetry:
  stats:
    intel_gpu_stats: true      # GPU load on the System page (set false on NVIDIA)
  version_check: true          # lets the HA integration report new versions

cameras:
  cam_01:
    ffmpeg:
      inputs:
        - path: rtsp://127.0.0.1:8554/cam_01        # go2rtc restream: one connection per camera
          input_args: preset-rtsp-restream
          roles: [record]
        - path: rtsp://127.0.0.1:8554/cam_01_sub
          input_args: preset-rtsp-restream
          roles: [detect, audio]
    detect:
      width: 1280            # match the sub stream resolution
      height: 720
  # cam_02 / cam_03: same as cam_01 with their own stream names
config.yaml (detector + decode)
detectors:
  ov:
    type: openvino
    device: GPU

model:
  model_type: yolo-generic
  width: 320
  height: 320
  input_tensor: nchw
  input_dtype: float
  path: /config/model_cache/yolov9-s-320.onnx
  labelmap_path: /labelmap/coco-80.txt

ffmpeg:
  hwaccel_args: preset-vaapi
  output_args:
    record: preset-record-generic-audio-copy

input_pixel_format is left at its default (RGB), which is what YOLOv9 wants; the bgr setting you'll see in older SSDLite examples is SSD-specific.

config.yaml (detector + decode)
detectors:
  onnx:
    type: onnx

model:
  model_type: yolo-generic
  width: 640
  height: 640
  input_tensor: nchw
  input_dtype: float
  path: /config/model_cache/yolov9-m-640.onnx
  labelmap_path: /labelmap/coco-80.txt

ffmpeg:
  hwaccel_args: preset-nvidia
  output_args:
    record: preset-record-generic-audio-copy

The -tensorrt image detects the GPU and runs the ONNX model through TensorRT automatically.

Per-camera software decode

One camera's H.265 sub stream kept failing VAAPI decode on the UHD 630 (hardware accelerator failed to decode picture, Could not find ref with POC), restarting ffmpeg every few minutes and dropping frames each time. Recording uses stream copy and needs no decode, so only detection was affected. The fix is to turn hardware decode off for that camera only; software-decoding one 720p stream at 5 fps is trivial for the CPU:

cameras:
  cam_03:
    ffmpeg:
      hwaccel_args: ''      # software decode for this camera; others keep preset-vaapi

Enrichments

All of these run fine alongside detection on the iGPU host. The small models are a deliberate choice: the UHD 630 is shared by decode and detection, so the enrichments run on the CPU instead of competing for it.

face_recognition:
  enabled: true
  model_size: small        # FaceNet on CPU; 'large' (ArcFace) wants a real GPU/NPU
  min_area: 500

semantic_search:
  enabled: true
  model_size: small        # quantized CPU model; quality difference is negligible
  model: jinav1

audio:
  enabled: true
  min_volume: 500
  listen: [bark, fire_alarm, scream, speech, yell, glass_breaking, crying, siren]

classification:
  bird:
    enabled: true          # built-in model, no training

Don't track face

With a COCO model, face detection for face_recognition runs automatically when a person is detected. Adding face to objects.track is only for Frigate+ or custom models that detect faces natively.

Zones, masks and stationary objects

The per-camera zones, objects.filters.<label>.mask and detect.stationary settings that stop parked cars from alerting are tuned against real footage, so they're written up with the reasoning in Lessons learned. Draw zones and masks in the Frigate UI (Settings > Masks / Zones); Frigate writes them back into config.yaml.

Start Frigate

cd /path/to/frigate/stack
docker compose up -d && docker logs -f frigate

On Intel, OpenVINO compiles the model for the GPU when Frigate starts, which can take up to a minute. On NVIDIA, first boot takes several minutes while TensorRT builds an optimized engine from the ONNX model (one-time per model).

Get Initial Password

docker logs frigate 2>&1 | grep -i password

The admin password is only printed on first startup. Change it under Settings > Users after logging in. If you missed it or are locked out, set this, restart, and read the new password from the logs (then remove it again):

auth:
  reset_admin_password: true

UI accessible at http://<YOUR_SERVER_IP>:8971 (https:// if you leave Frigate's TLS on).


7. Firewall Rules

IoT Interface (OPNsense)

Order matters -- first match wins.

# Action Source Destination Port Description
1 Pass IOT net <IOT_GATEWAY> 53 Force DNS to AdGuard
2 Block IOT net PrivateNetworks alias any Block IoT to all internal
3 Pass IOT net any any IoT internet access

Rule 1 from the original setup (Allow IoT cameras → Frigate host, port any) was identified as unnecessary and overly permissive. OPNsense is a stateful firewall -- return traffic from cameras to Frigate is handled automatically via state table. Only Frigate initiating outbound RTSP needs an explicit rule, which lives on the SERVER interface.

Cameras don't need the internet

Consider blocking the cameras' internet access entirely (rule 3) once they're set up. Frigate pulls the streams locally and the cameras only need NTP, which you can point at the gateway.

SERVER Interface (OPNsense)

# Action Source Destination Port Description
1 Pass <YOUR_SERVER_IP> (Frigate host) IOT net 554 Frigate → cameras RTSP
2 Block SERVER net LAN net any Block SERVER to LAN
3 Block SERVER net MGMT net any Block SERVER to MGMT
4 Pass SERVER net <SERVER_GATEWAY> 53 Force DNS to AdGuard
5 Pass SERVER net any any SERVER internet access

Force DNS rule must be above the internet access pass rule -- first match wins and a catch-all pass above it would bypass DNS enforcement entirely.

Keep firewall rules pointed at the Frigate host's current IP: if you move Frigate to another machine, update rule 1 and delete the old one rather than leaving a stale allow rule behind.


8. Home Assistant Integration

Install Frigate Integration (HACS)

The official Frigate integration is HACS-only -- it is not in the built-in HA integration store.

  1. In HA: HACS > Integrations > Search "Frigate" > Install
  2. Restart Home Assistant
  3. Settings > Devices & Services > Add Integration > Frigate
  4. Enter: http://<YOUR_SERVER_IP>:8971 plus a Frigate username and password

Use http:// with Frigate's TLS disabled (as in the config above) -- the integration can't verify Frigate's self-signed cert.

Update the integration with Home Assistant

HACS integrations aren't covered by your image pins. A monthly HA release can break the Frigate integration (entities fail to load, Error adding entity), so update it from HACS right after upgrading HA, and check the integration's release notes for the HA version it needs.

What You Get

After integration: - Camera entities for each cam (camera.cam_01 etc.) with live view - Binary sensors for each detected object class per camera - sensor.frigate_cam_01_person_count etc. - Event entities for alerts and detections - Full clip and snapshot access from HA

MQTT

Frigate communicates detections to HA via MQTT (Mosquitto). The HA MQTT integration must already be configured, and Frigate needs its own broker user (frigate in the Mosquitto ACL). Frigate publishes under the frigate/ topic prefix; frigate/available reads online once it's connected.

Confirm Frigate is publishing before troubleshooting the HA side (on the broker host):

docker exec -it mosquitto mosquitto_sub -u homeassistant -P '<MQTT_HA_PASSWORD>' -t 'frigate/available' -C 1 -v

Notifications are built on top of this: see Frigate + HA Notifications.


9. Issues?

Issue Root Cause Fix
Camera not in ARP/DHCP Dahua ships with static 192.168.1.108, not DHCP Access at 192.168.1.108, set static IoT IP manually
RTSP URL 404 / empty ffprobe Special chars in password break URL parsing Use alphanumeric-only camera passwords
Sub stream capped at 704x480 Sub Stream 1 is firmware-limited by design Use Sub Stream 2 (subtype=2)
ffmpeg restarting every few minutes, failed to decode picture VAAPI H.265 decode unstable on one stream (older iGPU) hwaccel_args: '' on that camera only
MQTT disconnected / Not authorized Missing or wrong MQTT user/password Set mqtt.user + password: "{FRIGATE_MQTT_PASSWORD}"; check the broker log
Masked object alerts again after a model swap Masks test the box's bottom-centre, which moves with the model Redraw masks; see Lessons learned
libnvcuvid.so.1 missing (NVIDIA) Debian's nvidia-driver doesn't pull in NVDEC sudo apt install libnvcuvid1
Docker broke after nvidia-ctk nvidia-ctk writes daemon.json, conflicts with -H fd:// Remove daemon.json, use deploy.resources in compose
ONNX loading None Frigate 0.16+ ships no YOLO models Build the YOLOv9 ONNX (section 4)
onnx-simplifier build fails Unpinned resolves to 0.5.0 which pulls broken onnxsim 0.6.2 Pin to onnx-simplifier==0.4.*
Firefox crashes on stream Snap-packaged Firefox sandbox blocks WebSocket Use a deb/Flatpak Firefox (e.g. Mozilla's APT repo) instead of the snap
Force DNS rule ineffective Rule was below internet pass rule Move Force DNS above internet access pass

Sources & further reading