Home Security with AI: Setting Up Frigate NVR
1
I wanted a self-hosted security camera system that could detect people and cars, record 24/7, and send notifications to my phone. No cloud services, no monthly subscriptions. This is how I set up Frigate NVR on my Linux server.
What Frigate Does
Frigate is an open-source NVR (Network Video Recorder) with real-time AI object detection. It can:
- Record 24/7 with configurable retention
- Detect objects (person, car, dog, etc.) using machine learning
- Filter alerts by zones (ignore street traffic, only alert for driveway)
- Send push notifications via browser/PWA
- Integrate with Home Assistant (optional)
Hardware Requirements
My setup:
- Server: Linux box with Intel iGPU (for hardware acceleration)
- Camera: Outdoor-rated IP camera with RTSP support and H.264 encoding
- Storage: 1+ TB for recording retention
The camera settings matter. I configured mine for:
- Main stream: H.264, 1080p for recording
- Sub stream: H.264, 704x480 for detection (lower res = faster processing)
Docker Compose Setup
Frigate runs in Docker with an MQTT broker for internal messaging:
services:
mqtt:
image: eclipse-mosquitto:2
container_name: mqtt
restart: unless-stopped
volumes:
- /mnt/bulk/frigate/mosquitto:/mosquitto
command: mosquitto -c /mosquitto-no-auth.conf
frigate:
image: ghcr.io/blakeblackshear/frigate:stable
container_name: frigate
restart: unless-stopped
shm_size: "512mb"
depends_on:
- mqtt
ports:
- "8971:8971" # Frigate UI (HTTPS)
- "127.0.0.1:5000:5000" # API (local only)
- "8554:8554" # RTSP restream
- "8555:8555/tcp" # WebRTC
- "8555:8555/udp" # WebRTC
devices:
- /dev/dri:/dev/dri # Intel iGPU for hardware accel
volumes:
- /etc/localtime:/etc/localtime:ro
- /mnt/bulk/frigate/config:/config
- /mnt/bulk/frigate/media:/media/frigateKey points:
shm_size: 512mbis required for ffmpeg frame buffers/dev/dripasses through the GPU for VAAPI hardware decoding- Port 5000 bound to localhost-only for security
The go2rtc Restreaming Pattern
This was the biggest stability improvement. Without go2rtc, Frigate opens separate connections to the camera for each role (recording, detection, live view). Many IP cameras don't handle multiple simultaneous connections well—streams drop, buffers overflow, and you get black screens.
go2rtc solves this by pulling from the camera once and restreaming locally:
go2rtc:
streams:
front: "rtsp://user:[email protected]:554/cam/realmonitor?channel=1&subtype=0"
front_sub: "rtsp://user:[email protected]:554/cam/realmonitor?channel=1&subtype=1"
webrtc:
candidates:
- 10.0.0.111:8555 # LAN IP
- stun:stun.l.google.com:19302Then the camera config uses the local restream instead of connecting directly:
cameras:
front:
ffmpeg:
inputs:
- path: rtsp://127.0.0.1:8554/front
input_args: preset-rtsp-restream
roles: [record]
- path: rtsp://127.0.0.1:8554/front_sub
input_args: preset-rtsp-restream
roles: [detect]Now the camera only has one client (go2rtc), and Frigate's internal processes all read from the local restream.
OpenVINO Object Detection
Frigate supports multiple detection backends. I used OpenVINO because it runs well on Intel CPUs/iGPUs:
detectors:
ov:
type: openvino
device: CPU
model:
width: 300
height: 300
input_tensor: nhwc
input_pixel_format: bgr
path: /openvino-model/ssdlite_mobilenet_v2.xml
labelmap_path: /openvino-model/coco_91cl_bkgr.txtThe model files come bundled in the Frigate container. If you see a "path should be string, not NoneType" error, you need to explicitly set the model path.
Detection and Recording Config
detect:
enabled: true
objects:
track:
- person
- car
filters:
person:
threshold: 0.5
min_score: 0.5
car:
threshold: 0.5
min_score: 0.5
cameras:
front:
detect:
enabled: true
width: 704
height: 480
fps: 5
record:
enabled: true
retain:
days: 7
mode: all
snapshots:
enabled: true
retain:
default: 7Important: detect.enabled: true must be set both globally AND per-camera. I spent a while debugging why detection wasn't working. The API stats showed detection_enabled: false even though the detector was configured.
Motion Masks (Fixing Timestamp Spam)
My camera has an on-screen timestamp. The changing seconds triggered constant motion detection. The fix: add a motion mask over the timestamp area:
cameras:
front:
motion:
mask: 0.662,0.031,0.659,0.087,0.979,0.085,0.981,0.031Coordinates are normalized (0-1 range). You can draw masks visually in the Frigate UI.
Zone Filtering (Ignoring Street Traffic)
The camera sees the street. Cars driving by triggered constant alerts. The solution: create a zone for just the yard/driveway and require alerts to originate from that zone:
cameras:
front:
zones:
front_yard:
coordinates: 0.002,0.822,0.31,0.831,0.569,0.828,1,0.854,1,1,0.002,0.999
objects:
- car
- person
review:
alerts:
required_zones:
- front_yardNow I only get alerts when someone/something enters my property, not when cars pass on the street.
Remote Access with Tailscale
I wanted a memorable URL to access Frigate instead of raw IP addresses. Tailscale Serve gives you a clean .ts.net hostname with a trusted certificate:
Tailscale Serve proxies the local service through your tailnet:
sudo tailscale serve reset
sudo tailscale serve --bg --set-path=/ https+insecure://127.0.0.1:8971The https+insecure:// prefix tells Tailscale that Frigate's internal port uses HTTPS with a self-signed certificate. After this, Frigate is accessible at https://myserver.tail12345.ts.net/ from any device on my tailnet, with a valid certificate.
iPhone Notifications (PWA)
For push notifications on iPhone:
- Open Frigate in Safari using the Tailscale HTTPS URL
- Tap Share → Add to Home Screen
- Open from the Home Screen icon
- Restart the Frigate container:
docker compose restart frigate
The PWA context is required for Safari push notifications to work. Regular browser tabs won't receive them.
Debugging Commands
Check if go2rtc streams are healthy:
sudo docker exec -it frigate sh -lc \
'curl -s http://127.0.0.1:1984/api/streams'Check Frigate stats (detection FPS, cameras):
curl -s http://127.0.0.1:5000/api/stats | python3 -m json.toolWatch logs for common issues:
sudo docker logs --tail 200 frigate | grep -Ei "error|traceback|ffmpeg"What's Next
The current setup prioritizes stability over optimization. A few things I might explore later:
- Direct record stream: If my camera supports more concurrent connections, I could skip go2rtc for the record stream and connect directly. This would reduce CPU usage on the server.
- GPU detection: Currently using OpenVINO on CPU. Moving detection to my Intel iGPU could free up CPU cycles for other tasks.
- More cameras: Adding more cameras for better coverage. The current go2rtc pattern scales well.
- Home Assistant integration: For a unified dashboard with weather, time, and camera feeds on a wall-mounted tablet.
Final Result
After about a week of tuning:
- 24/7 recording with 7-day rolling retention
- Person/car detection with bounding boxes
- Zone-filtered alerts (yard only, not street)
- Push notifications to iPhone
- Stable live view via go2rtc restreaming
- Audio in clips via AAC recording preset
All running on my home server with no cloud dependencies.