Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save Sillium/97b12102051b1dd9fb45a6919799b2cc to your computer and use it in GitHub Desktop.

Select an option

Save Sillium/97b12102051b1dd9fb45a6919799b2cc to your computer and use it in GitHub Desktop.
Frigate 0.17 in Apple Container with Silicon detector

How-To: Frigate on Apple Container with Apple Silicon NPU

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.

Prerequisites

  • Mac with Apple Silicon (M1/M2/M3/M4)
  • macOS 15+ (Sequoia) with Apple Container CLI
  • Python 3.11+
  • RTSP-compatible cameras
  • MQTT broker (optional)

Architecture

┌─────────────────────────┐     ┌──────────────────────┐
│  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)

Step 1: Prepare the YOLO Model

Option A: Use a pre-compiled model

Download yolov9-t-320.onnx from the FrigateDetector project releases.

Option B: Compile the model

# 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-export

Step 2: Install FrigateDetector

FrigateDetector is a macOS application that:

  • Loads the ONNX model
  • Listens on ZMQ (port 5555)
  • Runs inference via CoreML/Neural Engine

Structure

~/frigate/
├── FrigateDetector.app/
│   └── Contents/
│       ├── MacOS/FrigateDetector
│       └── Resources/app/
│           ├── detector/zmq_onnx_client.py
│           ├── models/yolo.onnx
│           ├── venv/
│           └── run.sh

Copy the model

mkdir -p ~/frigate/FrigateDetector.app/Contents/Resources/app/models
cp yolov9-t-320.onnx ~/frigate/FrigateDetector.app/Contents/Resources/app/models/yolo.onnx

Step 3: Configure Frigate

Create the directory structure

mkdir -p ~/frigate/{config/model_cache,storage}
cp yolov9-t-320.onnx ~/frigate/config/model_cache/yolo.onnx

Minimal configuration (~/frigate/config/config.yml)

mqtt:
  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

Important notes

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

Step 4: Configure NAT

Apple Container uses an isolated bridge network (192.168.64.x). To access cameras on other networks, you need to configure NAT.

Identify your network interfaces

# List interfaces
networksetup -listallhardwareports

# Typically:
# en0 = Ethernet
# en1 = Wi-Fi

Create NAT rules

cat > ~/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)
EOF

Enable NAT

sudo cp ~/frigate/pf-nat-rules.conf /etc/pf.anchors/frigate-nat.conf
sudo pfctl -f /etc/pf.anchors/frigate-nat.conf -e

Make NAT persistent (LaunchDaemon)

cat > ~/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.plist

Step 5: NAS Storage (optional)

To store recordings on a Synology NAS via NFS.

Configure NFS share on Synology

  1. DSM > Control Panel > Shared Folder
  2. Select the folder, NFS Permissions tab
  3. 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

Mount NFS on the Mac

# 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

Make mount persistent (fstab)

# 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 0

Options explained:

  • bg: mount in background if NAS unavailable at boot
  • soft: timeout instead of blocking indefinitely
  • intr: allows interrupting blocked operations
  • tcp: more reliable than UDP

Configure Frigate for NAS

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"

Step 6: Frigate Startup Script

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.sh

Note: Port 8971 with authentication. Port 5000 (no auth) is not exposed.


Step 7: FrigateDetector Auto-start

Create the LaunchAgent

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

Manual control

# Start
launchctl start com.frigate.detector

# Stop
launchctl stop com.frigate.detector

# View logs
tail -f ~/frigate/detector.log

Step 8: Container Auto-restart

Apple Container does not support --restart=always like Docker. Solution: a watch script.

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.sh

Watch LaunchAgent

cat > ~/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

Utility scripts

# 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.sh

Usage:

~/frigate/frigate-stop.sh   # Full stop (watch + container)
~/frigate/frigate-start.sh  # Start (enables watch which starts container)

Step 9: Container System Auto-start

Apple Container requires the system service to be started before containers can be launched.

Create the LaunchDaemon

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.plist

Manual control

container system start   # Start the service
container system stop    # Stop the service

Step 10: Startup and Verification

Startup order

  1. Container System (auto-starts at boot via LaunchDaemon)
  2. NAT (auto-starts at boot via LaunchDaemon)
  3. FrigateDetector (auto-starts at login via LaunchAgent)
  4. Frigate Watch (auto-starts at login, launches container)

Verification

# 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')
"

Expected result

{
  "detectors": {
    "apple_silicon": {
      "inference_speed": 11.5
    }
  }
}

Troubleshooting

Container cannot reach cameras

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>

"Model not ready" in Frigate logs

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.sh

MQTT disconnects in a loop

Symptom: 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_container

Slow inference (>100ms)

Symptom: Very high inference time instead of ~11ms.

# Check provider being used
grep -i "provider" ~/frigate/detector.log
# Should display: CoreMLExecutionProvider

If the provider is not CoreML, check the coremltools installation in the venv.

SHM too small (memory errors)

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)

---


@jctull
Copy link

jctull commented Jan 3, 2026

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!

@jctull
Copy link

jctull commented Jan 3, 2026

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!

Resolved with:

auth:
reset_admin_password: true

in the config.yml and running 'container logs -f frigate' in the terminal during startup.

@jctull
Copy link

jctull commented Jan 14, 2026

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment