Skip to content

Instantly share code, notes, and snippets.

@pmuir
Created May 21, 2026 11:11
Show Gist options
  • Select an option

  • Save pmuir/26aefd193fa0b4efb7d5febf824eaf77 to your computer and use it in GitHub Desktop.

Select an option

Save pmuir/26aefd193fa0b4efb7d5febf824eaf77 to your computer and use it in GitHub Desktop.
frigate
# Frigate NVR on MacBook Pro M4 — Setup Guide
Based on the official Frigate documentation and Apple Silicon Detector project.
**Sources:**
- https://docs.frigate.video/frigate/installation/#macos---apple-silicon
- https://docs.frigate.video/configuration/object_detectors#apple-silicon-detector
- https://github.com/frigate-nvr/apple-silicon-detector
---
## Architecture
The NPU (Neural Engine) in Apple Silicon can't be accessed from inside a container. So the setup uses two components:
1. **Frigate** — runs in a Docker container (Linux ARM64)
2. **Apple Silicon Detector** — runs natively on macOS, accesses the Neural Engine via CoreML
They communicate over ZMQ on port 5555. Frigate sends frames to the detector, which returns inference results.
```
┌──────────────────────┐ ┌──────────────────────┐
│ Docker Container │ ZMQ │ Native macOS │
│ Frigate (NVR) │◄─────►│ Apple Silicon │
│ │ :5555 │ Detector (ONNX) │
│ Port 8971 (Web UI) │ │ ▼ │
│ Port 8554 (RTSP) │ │ Neural Engine │
└──────────────────────┘ └──────────────────────┘
RTSP Cameras (x7)
```
---
## Step 1: Prepare macOS
### Disable AirPlay Receiver
macOS uses port 5000 for AirPlay Receiver, which conflicts with Frigate's internal API port.
**System Settings → General → AirDrop & Handoff → AirPlay Receiver → Off**
### Prevent sleep (clamshell server mode)
```bash
sudo pmset -a disablesleep 1
```
To reverse later: `sudo pmset -a disablesleep 0`
### Cap battery charge at 80%
**System Settings → Battery → Battery Health → Charging Optimisation** → set charge limit to 80%.
### Enable auto-login
LaunchAgents require a logged-in user session.
**System Settings → Users & Groups → Login Options → Automatic login** → select your user.
### Set a static IP or DHCP reservation
Your Mac's IP needs to be stable for camera access. Set a static IP or configure a DHCP reservation on your router.
---
## Step 2: Install Docker Desktop or OrbStack
The official Frigate docs support two container runtimes on macOS:
| | Docker Desktop | OrbStack |
|---|---|---|
| Type | Docker's official app | Native Swift app |
| Inference speed | Same | Same |
| CPU / power usage | Higher | Lower |
| Container start time | Faster | Similar |
| Docker Compose | Yes | Yes |
| restart policies | Yes | Yes |
| Cost | Free (personal) | Free (personal) |
**OrbStack** is the lighter option for a 24/7 server. Install from https://orbstack.dev
**Docker Desktop** is the more established option. Install from https://www.docker.com/products/docker-desktop/
The rest of this guide uses standard Docker Compose syntax that works with either.
---
## Step 3: Install the Apple Silicon Detector
The detector runs natively on macOS (outside Docker).
1. Go to https://github.com/frigate-nvr/apple-silicon-detector/releases
2. Download the latest `FrigateDetector.app.zip` (currently v1.1.1)
3. Unzip and move it somewhere permanent:
```bash
mkdir -p ~/frigate
mv ~/Downloads/FrigateDetector.app ~/frigate/
```
4. On first run, right-click the app → **Open** to bypass Gatekeeper. Alternatively, open it from Terminal:
```bash
~/frigate/FrigateDetector.app/Contents/MacOS/FrigateDetector
```
If macOS blocks it, go to **System Settings → Privacy & Security** and click "Open Anyway", then retry.
5. A Terminal window will appear and automatically create a virtual environment, install dependencies, and start the detector with `--model AUTO`.
6. Confirm it starts without errors, then close it (Ctrl+C). We'll set up auto-start later.
**Note:** No manual model download is needed. Frigate automatically loads the model and sends it to the detector via ZMQ.
---
## Step 4: Create Frigate Directory Structure
```bash
mkdir -p ~/frigate/{config,storage}
```
---
## Step 5: Download the YOLO Model
Download a YOLOv9 model. You can export one using Docker:
```bash
docker build . --build-arg MODEL_SIZE=t --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
```
This produces `yolov9-t-320.onnx` in your current directory. Copy it into the Frigate config:
```bash
mkdir -p ~/frigate/config/model_cache
cp yolov9-t-320.onnx ~/frigate/config/model_cache/yolo.onnx
```
---
## Step 6: Calculate Shared Memory Size
Frigate uses shared memory for frame processing. The formula per camera is:
```
(width × height × 1.5 × 20 + 270480) / 1048576 MB
```
Plus ~40 MB for logs.
| Detect resolution | 7 cameras + logs |
|---|---|
| 640×480 | ~103 MB |
| 1280×720 | ~226 MB |
Setting `shm_size: "512mb"` in docker-compose gives comfortable headroom.
---
## Step 7: Create docker-compose.yml
Create `~/frigate/docker-compose.yml`:
```yaml
services:
frigate:
container_name: frigate
image: ghcr.io/blakeblackshear/frigate:stable-standard-arm64
restart: unless-stopped
shm_size: "512mb"
volumes:
- /etc/localtime:/etc/localtime:ro
- ./config:/config
- ./storage:/media/frigate
- type: tmpfs
target: /tmp/cache
tmpfs:
size: 1000000000
ports:
- "8971:8971"
- "5001:5000" # Remapped from 5000 to avoid AirPlay conflict
- "8554:8554" # RTSP restream
- "8555:8555/tcp" # WebRTC
- "8555:8555/udp" # WebRTC
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
FRIGATE_RTSP_PASSWORD: "password"
```
Key points from the official docs:
- **Image**: `stable-standard-arm64` is the recommended image for Apple Silicon
- **extra_hosts**: The `host.docker.internal:host-gateway` mapping is required so the container can reach the Apple Silicon Detector running on the host
- **Port 5001:5000**: Remapped because macOS AirPlay uses port 5000
- **tmpfs on /tmp/cache**: In-memory filesystem for recording segment cache, recommended by the docs
---
## Step 8: Create Frigate Config
Create `~/frigate/config/config.yml`:
```yaml
version: 0.17-0
mqtt:
enabled: false # Set to true and configure if using Home Assistant
detectors:
apple-silicon:
type: zmq
endpoint: tcp://host.docker.internal:5555
model:
model_type: yolo-generic
width: 320
height: 320
input_tensor: nchw
input_dtype: float
path: /config/model_cache/yolo.onnx
labelmap_path: /labelmap/coco-80.txt
ffmpeg:
hwaccel_args: []
go2rtc:
streams:
# Replace with your actual camera RTSP URLs
camera1:
- rtsp://USER:PASS@CAM1_IP:554/stream
camera1_sub:
- rtsp://USER:PASS@CAM1_IP:554/stream_sub
camera2:
- rtsp://USER:PASS@CAM2_IP:554/stream
camera2_sub:
- rtsp://USER:PASS@CAM2_IP:554/stream_sub
# Repeat for cameras 3–7
cameras:
camera1:
enabled: true
ffmpeg:
inputs:
- path: rtsp://127.0.0.1:8554/camera1
input_args: preset-rtsp-restream
roles:
- record
- path: rtsp://127.0.0.1:8554/camera1_sub
input_args: preset-rtsp-restream
roles:
- detect
detect:
enabled: true
width: 640
height: 480
objects:
track:
- person
- car
- dog
- cat
record:
enabled: true
alerts:
retain:
days: 10
detections:
retain:
days: 10
# Repeat camera block for cameras 2–7
```
Configuration notes from the official docs:
| Setting | Value | Why |
|---|---|---|
| `detectors.type` | `zmq` | Required for Apple Silicon detector |
| `endpoint` | `tcp://host.docker.internal:5555` | Official recommended endpoint from the docs |
| `model` | Root level in YAML | Must NOT be nested inside `detectors` |
| `labelmap_path` | `/labelmap/coco-80.txt` | 80-class COCO subset, included in the container |
| `hwaccel_args` | `[]` | No GPU passthrough available in Docker on macOS |
| `objects.track` | COCO classes only | `license_plate` is not a COCO class — don't include it |
---
## Step 9: Start Everything
### Start the Apple Silicon Detector first
```bash
open ~/frigate/FrigateDetector.app
```
Or from Terminal:
```bash
~/frigate/FrigateDetector.app/Contents/MacOS/FrigateDetector
```
Wait until you see it listening on port 5555.
### Start Frigate
```bash
cd ~/frigate
docker compose up -d
```
### Get the admin password
On first run, Frigate generates a random admin password:
```bash
docker logs frigate 2>&1 | grep -i password
```
### Access the web UI
Open **http://localhost:8971** and log in with username `admin` and the password from the logs.
---
## Step 10: Verify
```bash
# Check the detector is running
pgrep -f zmq_onnx_client && echo "Detector OK"
# Check Frigate container
docker ps | grep frigate
# Check inference speed
curl -s http://localhost:5001/api/stats | python3 -c "
import sys, json
data = json.load(sys.stdin)
for name, det in data.get('detectors', {}).items():
speed = det.get('inference_speed', 0)
print(f'{name}: {speed:.1f}ms')
"
```
Expected: ~11ms on M4, ~13ms on M2.
---
## Step 11: Auto-Start on Login
### Apple Silicon Detector — LaunchAgent
Create `~/Library/LaunchAgents/com.frigate.detector.plist`:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.frigate.detector</string>
<key>ProgramArguments</key>
<array>
<string>/Users/YOUR_USERNAME/frigate/FrigateDetector.app/Contents/MacOS/FrigateDetector</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/Users/YOUR_USERNAME/frigate/detector.log</string>
<key>StandardErrorPath</key>
<string>/Users/YOUR_USERNAME/frigate/detector.log</string>
</dict>
</plist>
```
Replace `YOUR_USERNAME` with your macOS username, then load:
```bash
launchctl load ~/Library/LaunchAgents/com.frigate.detector.plist
```
### Frigate container
The `restart: unless-stopped` policy in docker-compose handles this automatically. Docker Desktop / OrbStack starts at login, and the container restarts with it. No additional configuration needed.
---
## Step 12: Rack & Hardware Setup
### Rack mounting
- Use a 1U or 2U vented cantilever shelf
- Place MacBook Pro horizontal in clamshell mode
- Use small rubber feet or a silicone mat underneath for an airflow gap
- Orient the hinge (rear vents) toward the rear of the rack
### Cupboard ventilation
- **Extract air out** — mount the fan high in the cupboard
- Cool air draws in naturally at the bottom, warm air is pulled out at the top
- This follows the same principle as server room hot-aisle containment
### Networking
- Use a USB-C to Ethernet adapter for reliable wired connectivity
- Ensure cameras are on the same network/VLAN as the Mac
### Power
- Keep power adapter permanently connected
- Battery charge capped at 80% (Step 1)
- Optional: HDMI dummy plug if you want true clamshell mode without the `pmset` approach
---
## Storage Planning
7 cameras recording continuously will use significant storage:
| Recording mode | Approx. daily usage (7 cameras) |
|---|---|
| Detection clips only | Minimal (~1–5 GB) |
| Continuous at moderate quality | ~70–210 GB/day |
Options:
1. **External USB-C SSD** — simplest; update the `storage` volume mount path
2. **NAS via NFS/SMB** — offload to network storage; add to `/etc/fstab` for persistence
3. **Internal SSD** — fine for short retention periods on a 1TB+ MacBook
Adjust `record.retain.days` to manage disk usage.
---
## Useful Commands
```bash
# View Frigate logs
docker logs -f frigate
# View detector logs
tail -f ~/frigate/detector.log
# Restart Frigate container
cd ~/frigate && docker compose restart
# Stop everything
cd ~/frigate && docker compose down
# Update Frigate
cd ~/frigate && docker compose pull && docker compose up -d
# Check thermals
sudo powermetrics --samplers smc -i 5000 -n 1
# Re-enable sleep when decommissioning
sudo pmset -a disablesleep 0
```
---
## Troubleshooting
### "Model not ready" in Frigate logs
The detector must be running before Frigate starts. Check `~/frigate/detector.log`, restart the detector, then restart Frigate.
### Slow inference (>100ms)
Check the detector log for `CoreMLExecutionProvider`. If it's falling back to CPU, there's a CoreML setup issue. Run the detector with verbose logging:
```bash
~/frigate/FrigateDetector.app/Contents/MacOS/FrigateDetector --verbose
```
### Container can't reach cameras
Docker Desktop / OrbStack route traffic through the Mac's network stack. If the Mac can ping the cameras, the container should be able to reach them too. Check VLANs and firewall rules.
### Port 5000 still blocked
Confirm AirPlay Receiver is disabled. If you need AirPlay, the docker-compose already remaps to 5001.
### "Bus error" / shared memory issues
Increase `shm_size` in docker-compose.yml. See the calculation in Step 6.
### First login — can't find password
```bash
docker logs frigate 2>&1 | grep -i password
```
Or add this temporarily to `config.yml` to reset it:
```yaml
auth:
reset_admin_password: true
```
---
## References
- [Frigate Installation — macOS Apple Silicon](https://docs.frigate.video/frigate/installation/#macos---apple-silicon)
- [Frigate Object Detectors — Apple Silicon](https://docs.frigate.video/configuration/object_detectors#apple-silicon-detector)
- [Apple Silicon Detector GitHub](https://github.com/frigate-nvr/apple-silicon-detector)
- [Frigate Getting Started Guide](https://docs.frigate.video/guides/getting_started)
- [Frigate Full Reference Config](https://docs.frigate.video/configuration/reference)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment