Created
May 21, 2026 11:11
-
-
Save pmuir/26aefd193fa0b4efb7d5febf824eaf77 to your computer and use it in GitHub Desktop.
frigate
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 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