Introduction

Running Frigate NVR on Proxmox with a Google Coral USB TPU is a powerful combination for local AI-based object detection. However, getting it working correctly — especially inside an LXC container with Docker — involves a number of non-obvious steps. In this post, I’ll walk through the complete setup, config, and troubleshooting steps that took my setup from broken to fully operational with 7 cameras, Coral TPU detection at 1% GPU usage, and recordings going directly to a QNAP NAS.

The Setup

My hardware and software stack:

  • Proxmox host: Intel i5-1135G7 (8 threads, 2.4GHz), 32GB RAM, Intel Xe iGPU
  • Frigate LXC: Debian 12, 4GB RAM, 4 cores, 20GB root disk
  • Detector: Google Coral USB TPU (plugged directly into Proxmox host)
  • Cameras: 7x Reolink cameras (P760 TrackMix, P850, P330, P324 x2, Video Doorbell, E1 4K WiFi) on VLAN 10
  • Storage: QNAP NAS via NFS mount at /mnt/qnap
  • Home Assistant: Running on separate VM at 192.168.1.46

Why Frigate Instead of BlueIris?

Previously I was running BlueIris on a Windows 11 Pro VM on Proxmox. It worked, but it was heavyweight — a full Windows VM just to run camera software. Frigate runs in a lightweight Debian LXC container, uses a fraction of the resources, integrates natively with Home Assistant, and supports the Google Coral TPU for fast local AI detection. Once Frigate was stable, the Win11Pro VM was shut down entirely.

Frigate Installation on Proxmox LXC

Frigate runs as a Docker container inside a Debian 12 LXC. The LXC was created using the Frigate Proxmox Script by saihgupr, which handles the Docker install and initial compose file setup.

Key LXC settings in /etc/pve/lxc/115.conf for GPU and USB access:

lxc.cgroup2.devices.allow: c 226:* rwm
lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir
lxc.apparmor.profile: unconfined
lxc.cgroup2.devices.allow: c 189:* rwm
lxc.mount.entry: /dev/bus/usb dev/bus/usb none bind,optional,create=dir 0,0
usb: vendorid=0x18d1,productid=0x9302
usb: vendorid=0x1a6e,productid=0x089a

The two usb: lines are critical — they pass the Coral through by vendor/product ID rather than by device path, which means it survives reboots even when the USB device number changes. The 1a6e:089a is the Coral in its uninitialized state, and 18d1:9302 is after it has been initialized by the EdgeTPU runtime.

Docker Compose Configuration

The docker-compose.yml needs two important device entries — the Intel iGPU for hardware video decoding, and the full USB bus for the Coral:

version: "3.9"
services:
  frigate:
    container_name: frigate
    restart: unless-stopped
    image: ghcr.io/blakeblackshear/frigate:stable
    volumes:
      - ./config:/config
      - /mnt/qnap:/media/frigate
      - type: tmpfs
        target: /tmp/cache
        tmpfs:
          size: 1000000000
    ports:
      - "5000:5000"
      - "8554:8554"
      - "8555:8555/tcp"
      - "8555:8555/udp"
      - "1984:1984"
      - "8971:8971"
    devices:
      - /dev/dri/renderD128:/dev/dri/renderD128
      - /dev/bus/usb:/dev/bus/usb
    environment:
      - FRIGATE_RTSP_PASSWORD=password
      - CONFIG_FILE=/config/config.yml
    cap_add:
      - CAP_PERFMON
    shm_size: "512mb"

Note the storage mount: /mnt/qnap:/media/frigate — this points recordings directly at the QNAP NAS NFS mount. The media_path config option was removed in Frigate 0.17, so this Docker volume mount is the correct way to set the recording destination.

Frigate config.yml

Here is the complete working config.yml for 7 Reolink cameras with Coral TPU detection:

mqtt:
  enabled: true
  host: 192.168.1.9
  port: 1883
  topic_prefix: frigate
  client_id: frigate
  stats_interval: 60

ffmpeg:
  global_args: -hide_banner -loglevel warning
  hwaccel_args: []
  input_args: preset-rtsp-restream
  output_args:
    record: preset-record-generic-audio-aac

detectors:
  coral:
    type: edgetpu
    device: usb

database:
  path: /config/frigate.db

detect:
  width: 1280
  height: 720
  fps: 5
  enabled: true

objects:
  track:
    - person
    - animal
    - cat
    - dog
  filters:
    person:
      min_area: 1500
      min_score: 0.6
      threshold: 0.7
    animal:
      min_area: 1000
      min_score: 0.5
      threshold: 0.6

record:
  enabled: true
  retain:
    days: 3
    mode: motion
  alerts:
    retain:
      days: 10
  detections:
    retain:
      days: 10

snapshots:
  enabled: true
  retain:
    default: 10
  bounding_box: true

go2rtc:
  streams:
    doorbell:
      - rtsp://user:pass@192.168.10.13:554/Preview_01_main
    driveway:
      - rtsp://user:pass@192.168.10.15:554/Preview_01_main
    garden:
      - rtsp://user:pass@192.168.10.17:554/Preview_01_main
    garage_back:
      - rtsp://user:pass@192.168.10.12:554/Preview_01_main
    dryroom:
      - rtsp://user:pass@192.168.10.14:554/Preview_01_main
    hallway:
      - rtsp://user:pass@192.168.10.16:554/Preview_01_main
    e1_wifi:
      - rtsp://user:pass@192.168.10.18:554/Preview_01_main

cameras:
  doorbell:
    enabled: true
    ffmpeg:
      inputs:
        - path: rtsp://user:pass@192.168.10.13:554/Preview_01_main
          roles: [record]
        - path: rtsp://user:pass@192.168.10.13:554/Preview_01_sub
          roles: [detect, audio]
    detect:
      width: 896
      height: 512
      fps: 5

  driveway:
    enabled: true
    ffmpeg:
      inputs:
        - path: rtsp://user:pass@192.168.10.15:554/Preview_01_main
          roles: [record]
        - path: rtsp://user:pass@192.168.10.15:554/Preview_01_sub
          roles: [detect, audio]
    objects:
      track: [person, animal, dog, cat, car]

  garden:
    enabled: true
    ffmpeg:
      inputs:
        - path: rtsp://user:pass@192.168.10.17:554/Preview_01_main
          roles: [record]
        - path: rtsp://user:pass@192.168.10.17:554/Preview_01_sub
          roles: [detect]

  garage_back:
    enabled: true
    ffmpeg:
      inputs:
        - path: rtsp://user:pass@192.168.10.12:554/Preview_01_main
          roles: [record]
        - path: rtsp://user:pass@192.168.10.12:554/Preview_01_sub
          roles: [detect]
    objects:
      track: [person, animal, dog, cat, car]

  dryroom:
    enabled: true
    ffmpeg:
      inputs:
        - path: rtsp://user:pass@192.168.10.14:554/Preview_01_main
          roles: [record]
        - path: rtsp://user:pass@192.168.10.14:554/Preview_01_sub
          roles: [detect]

  hallway:
    enabled: true
    ffmpeg:
      inputs:
        - path: rtsp://user:pass@192.168.10.16:554/Preview_01_main
          roles: [record]
        - path: rtsp://user:pass@192.168.10.16:554/Preview_01_sub
          roles: [detect]

  e1_wifi:
    enabled: true
    ffmpeg:
      inputs:
        - path: rtsp://user:pass@192.168.10.18:554/Preview_01_main
          roles: [record]
        - path: rtsp://user:pass@192.168.10.18:554/Preview_01_sub
          roles: [detect]
    detect:
      width: 640
      height: 480
      fps: 5

version: 0.17-0

Key Pitfalls and Fixes

1. Windows Line Endings (CRLF) Breaking YAML

If you paste config content from a Windows machine, you’ll get rn line endings that break YAML parsing with the cryptic error mapping values are not allowed in this context. Fix it with:

sed -i 's/r//' /opt/frigate/config/config.yml

2. Heredoc Artifacts in Config

Using cat > file << 'EOF' to write configs can leave the command itself or the EOF marker inside the file if something goes wrong. Always verify with cat -A config.yml | head -5 and clean up with:

sed -i '/^cat > /d' /opt/frigate/config/config.yml
sed -i '/^EOF$/d' /opt/frigate/config/config.yml

3. Coral USB Device Number Changes

The Coral USB changes its vendor ID after initialization (from 1a6e:089a to 18d1:9302) and also changes its device number on every reboot. Binding by device path (/dev/bus/usb/003/041) will break after reboot. The correct solution is to use vendor ID passthrough in the Proxmox LXC config:

usb: vendorid=0x18d1,productid=0x9302
usb: vendorid=0x1a6e,productid=0x089a

4. Coral USB Must Be Plugged Directly Into the Host

The Coral USB does not work reliably through a powered USB hub — even a good quality one. Plug it directly into a USB 3.0 port on the Proxmox server. This was the cause of several hours of debugging where the device was simply invisible to the host.

5. OpenVINO Detector Hangs Inside LXC

The Intel OpenVINO GPU detector (type: openvino, device: GPU) causes Frigate’s watchdog to repeatedly kill and restart the detection process when running inside a Proxmox LXC. The iGPU passthrough works for video decoding but not reliably for OpenVINO inference inside nested containers. Use the Coral instead, or fall back to CPU detection (type: cpu) if you don’t have a Coral.

6. media_path Removed in Frigate 0.17

The media_path top-level config option was removed in Frigate 0.17. Set your recording destination via the Docker volume mount instead: /your/nas/path:/media/frigate in docker-compose.yml.

7. Don’t Use the Frigate HA Add-on With an External Frigate Instance

If Frigate runs as a standalone VM or LXC, do not install the Frigate Full Access add-on in Home Assistant — it will try to run its own Frigate instance and cause HA to hang. Instead, install only the Frigate integration (Settings → Devices & Services) and point it at your external instance IP. The integration provides all HA entities, automations, and MQTT events without running a duplicate Frigate process.

Results

After completing this setup:

  • 7 cameras running stable, direct from Reolink (no BlueIris middleman)
  • Google Coral TPU handling all detection at Intel GPU 0%, CPU ~35% (CPU is just ffmpeg decoding)
  • Recordings going directly to QNAP NAS via NFS
  • Survives LXC reboots with Coral auto-detected on startup
  • Full Home Assistant integration for automations, alerts, and AI descriptions
  • Win11Pro/BlueIris VM decommissioned, freeing significant RAM on the Proxmox host

Conclusion

Running Frigate with a Coral TPU on Proxmox LXC is absolutely worth the setup effort. The combination delivers fast, local, private AI detection with minimal resource usage. The main gotchas are around the Coral USB passthrough (use vendor IDs, not device paths, and plug directly into the host) and the Frigate 0.17 config changes. Hopefully this post saves you the hours of debugging it took to figure these out.

Privacy Preference Center