This guide explains how to install Frigate 0.17 on Mac with Apple Silicon using Apple Container (native alternative to Docker) with detection via the Neural Engine.
- Mac with Apple Silicon (M1/M2/M3/M4)
- macOS 15+ (Sequoia) with Apple Container CLI
- Python 3.11+
- RTSP-compatible cameras
- MQTT broker (optional)
┌─────────────────────────┐ ┌──────────────────────┐
│ Apple Container │ │ Native macOS │
│ ┌───────────────────┐ │ ZMQ │ ┌────────────────┐ │
│ │ Frigate │◄─┼─────┼─►│ FrigateDetector│ │
│ │ (NVR) │ │:5555│ │ (ONNX) │ │
│ └───────────────────┘ │ │ └────────────────┘ │
│ 192.168.64.x │ │ ▼ │
└─────────────────────────┘ │ Neural Engine │
│ └──────────────────────┘
│ NAT (pfctl)
▼
RTSP Cameras
Why this architecture?
- Apple Container cannot access the Neural Engine (Linux isolation)
- FrigateDetector runs on native macOS to use CoreML
- Communication via ZMQ (tcp://IP:5555)
Download yolov9-t-320.onnx from the FrigateDetector project releases.
# Create the Dockerfile
cat > Dockerfile <<'EOF'
FROM python:3.11-slim
RUN apt-get update && apt-get install -y git cmake && rm -rf /var/lib/apt/lists/*
RUN pip install --no-cache-dir \
ultralytics \
onnx \
onnxsim
WORKDIR /models
# Export YOLOv9-t to ONNX 320x320
RUN yolo export model=yolov9t.pt format=onnx imgsz=320 simplify=True
CMD ["cp", "/models/yolov9t.onnx", "/output/yolov9-t-320.onnx"]
EOF
# Build and export
docker build -t yolo-export .
docker run -v $(pwd):/output yolo-exportFrigateDetector is a macOS application that:
- Loads the ONNX model
- Listens on ZMQ (port 5555)
- Runs inference via CoreML/Neural Engine
~/frigate/
├── FrigateDetector.app/
│ └── Contents/
│ ├── MacOS/FrigateDetector
│ └── Resources/app/
│ ├── detector/zmq_onnx_client.py
│ ├── models/yolo.onnx
│ ├── venv/
│ └── run.sh
mkdir -p ~/frigate/FrigateDetector.app/Contents/Resources/app/models
cp yolov9-t-320.onnx ~/frigate/FrigateDetector.app/Contents/Resources/app/models/yolo.onnxmkdir -p ~/frigate/{config/model_cache,storage}
cp yolov9-t-320.onnx ~/frigate/config/model_cache/yolo.onnxmqtt:
enabled: true
host: <MQTT_BROKER_IP> # Your MQTT broker IP
port: 1883
user: <MQTT_USER> # Optional
password: <MQTT_PASSWORD> # Optional
# ZMQ detector to FrigateDetector on macOS
detectors:
apple_silicon:
type: zmq
endpoint: tcp://<MAC_IP>:5555 # Mac IP (not localhost!)
# YOLO model
model:
model_type: yolo-generic
width: 320
height: 320
input_tensor: nchw
input_dtype: float
path: /config/model_cache/yolo.onnx
# No hardware acceleration in the container
ffmpeg:
hwaccel_args: []
go2rtc:
streams:
camera1:
- rtsp://<CAM_USER>:<CAM_PASSWORD>@<CAM_IP>:554/stream
camera1_sub:
- rtsp://<CAM_USER>:<CAM_PASSWORD>@<CAM_IP>:554/stream_sub
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
version: 0.17-0| Parameter | Value | Note |
|---|---|---|
endpoint |
tcp://<MAC_IP>:5555 |
Real Mac IP, not localhost or host.docker.internal |
model |
Root level | Not inside detectors |
objects.track |
COCO classes | Do not include license_plate (not supported by generic YOLO) |
hwaccel_args |
[] |
No hardware acceleration in Linux container |
Apple Container uses an isolated bridge network (192.168.64.x). To access cameras on other networks, you need to configure NAT.
# List interfaces
networksetup -listallhardwareports
# Typically:
# en0 = Ethernet
# en1 = Wi-Ficat > ~/frigate/pf-nat-rules.conf <<'EOF'
# NAT for Apple Container to external networks
# Adapt interfaces according to your configuration
# NAT via Ethernet (en0)
nat on en0 from 192.168.64.0/24 to any -> (en0)
# NAT via Wi-Fi (en1) - if needed
nat on en1 from 192.168.64.0/24 to any -> (en1)
EOFsudo cp ~/frigate/pf-nat-rules.conf /etc/pf.anchors/frigate-nat.conf
sudo pfctl -f /etc/pf.anchors/frigate-nat.conf -ecat > ~/frigate/com.frigate.nat.plist <<'EOF'
<?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.nat</string>
<key>ProgramArguments</key>
<array>
<string>/sbin/pfctl</string>
<string>-f</string>
<string>/etc/pf.anchors/frigate-nat.conf</string>
<string>-e</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>StandardErrorPath</key>
<string>/var/log/frigate-nat.log</string>
</dict>
</plist>
EOF
sudo cp ~/frigate/com.frigate.nat.plist /Library/LaunchDaemons/
sudo launchctl load /Library/LaunchDaemons/com.frigate.nat.plistTo store recordings on a Synology NAS via NFS.
- DSM > Control Panel > Shared Folder
- Select the folder, NFS Permissions tab
- Add a rule for the Mac IP (e.g., 10.0.10.4)
- Squash: No mapping or Map all users to admin
- Privilege: Read/Write
- Check: Allow connections from non-privileged ports
# Create mount point
sudo mkdir -p /Volumes/frigate-nas
# Manual mount (test)
sudo mount -t nfs -o rw,resvport 10.0.10.50:/volume1/pve/data/frigate /Volumes/frigate-nas
# Verify
touch /Volumes/frigate-nas/test.txt && rm /Volumes/frigate-nas/test.txt# Edit fstab (safe method)
sudo vifs
# Add this line:
10.0.10.50:/volume1/pve/data/frigate /Volumes/frigate-nas nfs rw,bg,soft,intr,tcp 0 0Options explained:
bg: mount in background if NAS unavailable at bootsoft: timeout instead of blocking indefinitelyintr: allows interrupting blocked operationstcp: more reliable than UDP
Modify start-frigate.sh to use NAS storage:
container run -d \
--name "$CONTAINER_NAME" \
-m 2048M \
-v ~/frigate/config:/config \
-v /Volumes/frigate-nas:/media/frigate \ # NAS instead of ~/frigate/storage
-p 8971:8971 \
-p 8554:8554 \
-p 8555:8555 \
"$IMAGE"Apple Container does not support --shm-size. Solution: remount /dev/shm after startup.
cat > ~/frigate/start-frigate.sh <<'EOF'
#!/bin/bash
export PATH="/usr/local/bin:/usr/bin:/bin:$PATH"
set -e
CONTAINER_NAME="frigate"
SHM_SIZE="512M"
IMAGE="ghcr.io/blakeblackshear/frigate:0.17.0-beta1-standard-arm64"
echo "=== Frigate Apple Container Launcher ==="
# Stop existing container
if container list 2>/dev/null | grep -q "$CONTAINER_NAME"; then
echo "Stopping existing container..."
container stop "$CONTAINER_NAME" 2>/dev/null || true
sleep 2
fi
if container list -a 2>/dev/null | grep -q "$CONTAINER_NAME"; then
container rm "$CONTAINER_NAME" 2>/dev/null || true
fi
# Launch container
echo "Starting Frigate..."
container run -d \
--name "$CONTAINER_NAME" \
-m 2048M \
-v ~/frigate/config:/config \
-v ~/frigate/storage:/media/frigate \
-p 8971:8971 \
-p 8554:8554 \
-p 8555:8555 \
"$IMAGE"
# Wait for startup
echo "Waiting for startup..."
sleep 5
# Verify container is running
if ! container list | grep -q "$CONTAINER_NAME"; then
echo "Error: Container failed to start"
exit 1
fi
# Remount /dev/shm with correct size
echo "Configuring SHM to $SHM_SIZE..."
container exec "$CONTAINER_NAME" mount -o remount,size="$SHM_SIZE" /dev/shm
# Verify
SHM_ACTUAL=$(container exec "$CONTAINER_NAME" df -h /dev/shm | tail -1 | awk '{print $2}')
echo "SHM configured to: $SHM_ACTUAL"
echo ""
echo "=== Frigate started ==="
echo "Web interface: http://localhost:8971 (with auth)"
echo "RTSP: rtsp://localhost:8554"
EOF
chmod +x ~/frigate/start-frigate.shNote: Port 8971 with authentication. Port 5000 (no auth) is not exposed.
cat > ~/frigate/com.frigate.detector.plist <<EOF
<?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>WorkingDirectory</key>
<string>$HOME/frigate/FrigateDetector.app/Contents/Resources/app</string>
<key>ProgramArguments</key>
<array>
<string>$HOME/frigate/FrigateDetector.app/Contents/Resources/app/venv/bin/python3</string>
<string>detector/zmq_onnx_client.py</string>
<string>--model</string>
<string>AUTO</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>$HOME/frigate/detector.log</string>
<key>StandardErrorPath</key>
<string>$HOME/frigate/detector.log</string>
</dict>
</plist>
EOF
# Install and enable
cp ~/frigate/com.frigate.detector.plist ~/Library/LaunchAgents/
launchctl load ~/Library/LaunchAgents/com.frigate.detector.plist# Start
launchctl start com.frigate.detector
# Stop
launchctl stop com.frigate.detector
# View logs
tail -f ~/frigate/detector.logApple Container does not support --restart=always like Docker. Solution: a watch script.
cat > ~/frigate/watch-frigate.sh <<'EOF'
#!/bin/bash
# Frigate container watch - Auto-restart if stopped
export PATH="/usr/local/bin:/usr/bin:/bin:$PATH"
CONTAINER_NAME="frigate"
CHECK_INTERVAL=10
LOG_FILE="$HOME/frigate/watch.log"
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
}
log "Starting Frigate watch"
while true; do
if ! container list 2>/dev/null | grep -q "$CONTAINER_NAME.*running"; then
log "Container stopped - Restarting..."
"$HOME/frigate/start-frigate.sh" >> "$LOG_FILE" 2>&1
if container list 2>/dev/null | grep -q "$CONTAINER_NAME.*running"; then
log "Container restarted successfully"
else
log "ERROR: Failed to restart"
fi
sleep 30
fi
sleep "$CHECK_INTERVAL"
done
EOF
chmod +x ~/frigate/watch-frigate.shcat > ~/frigate/com.frigate.watch.plist <<EOF
<?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.watch</string>
<key>ProgramArguments</key>
<array>
<string>$HOME/frigate/watch-frigate.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>$HOME/frigate/watch.log</string>
<key>StandardErrorPath</key>
<string>$HOME/frigate/watch.log</string>
</dict>
</plist>
EOF
cp ~/frigate/com.frigate.watch.plist ~/Library/LaunchAgents/
launchctl load ~/Library/LaunchAgents/com.frigate.watch.plist# Stop script
cat > ~/frigate/frigate-stop.sh <<'EOF'
#!/bin/bash
export PATH="/usr/local/bin:/usr/bin:/bin:$PATH"
echo "=== Stopping Frigate ==="
launchctl stop com.frigate.watch 2>/dev/null
if container list 2>/dev/null | grep -q "frigate.*running"; then
container stop frigate
fi
echo "=== Frigate stopped ==="
EOF
# Start script
cat > ~/frigate/frigate-start.sh <<'EOF'
#!/bin/bash
export PATH="/usr/local/bin:/usr/bin:/bin:$PATH"
echo "=== Starting Frigate ==="
launchctl start com.frigate.watch 2>/dev/null
sleep 15
if container list 2>/dev/null | grep -q "frigate.*running"; then
echo "=== Frigate started ==="
echo "Web interface: http://localhost:8971"
fi
EOF
chmod +x ~/frigate/frigate-stop.sh ~/frigate/frigate-start.shUsage:
~/frigate/frigate-stop.sh # Full stop (watch + container)
~/frigate/frigate-start.sh # Start (enables watch which starts container)Apple Container requires the system service to be started before containers can be launched.
sudo tee /Library/LaunchDaemons/com.apple.container.system.plist << 'EOF'
<?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.apple.container.system</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/container</string>
<string>system</string>
<string>start</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
EOF
sudo launchctl load /Library/LaunchDaemons/com.apple.container.system.plistcontainer system start # Start the service
container system stop # Stop the service- Container System (auto-starts at boot via LaunchDaemon)
- NAT (auto-starts at boot via LaunchDaemon)
- FrigateDetector (auto-starts at login via LaunchAgent)
- Frigate Watch (auto-starts at login, launches container)
# Check FrigateDetector
pgrep -f zmq_onnx_client && echo "✅ Detector OK"
# Check NAT
sudo pfctl -s nat | grep -q "192.168.64" && echo "✅ NAT OK"
# Check Frigate
container list | grep frigate && echo "✅ Container OK"
# Check inference stats
curl -s http://localhost:8971/api/stats | python3 -c "
import sys, json
data = json.load(sys.stdin)
speed = data['detectors']['apple_silicon']['inference_speed']
print(f'✅ Inference: {speed:.1f}ms')
"{
"detectors": {
"apple_silicon": {
"inference_speed": 11.5
}
}
}Symptom: Video stream unavailable, RTSP connection errors.
# Check NAT
sudo pfctl -s nat
# Reload rules
sudo pfctl -f /etc/pf.anchors/frigate-nat.conf -e
# Test connectivity from container
container exec frigate ping -c 3 <CAM_IP>Symptom: WARNING : Model not ready, returning zero detections
# Verify model exists
ls -la ~/frigate/FrigateDetector.app/Contents/Resources/app/models/yolo.onnx
# Check detector logs
tail -20 ~/frigate/detector.log
# Restart detector then Frigate
launchctl stop com.frigate.detector
launchctl start com.frigate.detector
sleep 3
~/frigate/start-frigate.shSymptom: MQTT connected followed by MQTT disconnected every second.
Probable cause: Two Frigate instances with the same MQTT client_id.
Solution: Stop the other instance or configure a unique client_id:
mqtt:
client_id: frigate_apple_containerSymptom: Very high inference time instead of ~11ms.
# Check provider being used
grep -i "provider" ~/frigate/detector.log
# Should display: CoreMLExecutionProviderIf the provider is not CoreML, check the coremltools installation in the venv.
Symptom: Shared memory errors, dropped frames.
# Check current size
container exec frigate df -h /dev/shm
# If < 512M, remount
container exec frigate mount -o remount,size=512M /dev/shm
---
## Apple Container vs Docker Limitations
| Feature | Docker | Apple Container | Solution |
|---------|--------|-----------------|----------|
| `--shm-size` | ✅ | ❌ | Manual remount after startup |
| `host.docker.internal` | ✅ | ❌ | Use real Mac IP |
| GPU/NPU access | ✅ (with config) | ❌ | External detector via ZMQ |
| Host network mode | ✅ | ❌ | NAT via pfctl |
| Named volumes | ✅ | ❌ | Bind mounts only |
---
## Performance
| Metric | M1 | M2 | M3 | M4 |
|--------|----|----|----|----|
| Inference (ms) | ~15 | ~13 | ~12 | ~11 |
| Provider | CoreML | CoreML | CoreML | CoreML |
| Neural Engine | ✅ | ✅ | ✅ | ✅ |
**Model configuration:**
- Model: YOLOv9-t
- Resolution: 320x320
- Format: ONNX
---
## File Structure
~/frigate/ ├── config/ │ ├── config.yml # Frigate configuration │ └── model_cache/ │ └── yolo.onnx # YOLO model for Frigate ├── storage/ # Video recordings ├── FrigateDetector.app/ # macOS detector application │ └── Contents/Resources/app/ │ ├── models/yolo.onnx # YOLO model for detector │ └── venv/ # Python environment ├── frigate-start.sh # Simple start ├── frigate-stop.sh # Full stop ├── start-frigate.sh # Container startup (used by watch) ├── watch-frigate.sh # Auto-restart watch ├── pf-nat-rules.conf # NAT rules ├── com.frigate.nat.plist # NAT LaunchDaemon ├── com.frigate.detector.plist # Detector LaunchAgent ├── com.frigate.watch.plist # Watch LaunchAgent ├── detector.log # Detector logs └── watch.log # Watch logs
/etc/pf.anchors/frigate-nat.conf # NAT rules (system copy) /Library/LaunchDaemons/com.apple.container.system.plist # Container System service /Library/LaunchDaemons/com.frigate.nat.plist # NAT service ~/Library/LaunchAgents/com.frigate.detector.plist # Detector service ~/Library/LaunchAgents/com.frigate.watch.plist # Watch service
---
## Resources
- [Frigate Documentation](https://docs.frigate.video/)
- [Frigate GitHub](https://github.com/blakeblackshear/frigate)
- [Apple Silicon Detector](https://github.com/frigate-nvr/apple-silicon-detector) - FrigateDetector for macOS
- [Apple Container Documentation](https://developer.apple.com/documentation/virtualization)
- [ZMQ Detector Discussion](https://github.com/blakeblackshear/frigate/discussions/21290)
---
I have been having so many problems with container freezes, inability to stop the container, etc., that I switched back to using the Home Assistant full access add-on. The Apple container software seems to need more work to make this a usable workflow for me, though I am keeping the Apple Silicon Detector startup in place. I have my HA instance pointing to my server and the native detector that way for now. This gist is still very useful, just skipping the container based elements for the time being.