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)
---
This is excellent! I am failing to figure out how to locate the default admin login/password once I get the container running. Any hints? I am not familiar with this, and poking around with "container logs frigate" seems not to make it apparent for me. Thanks for sharing this!