Google Coral USB TPU with Proxmox

Table of Contents:

Intro

I recently bought some RTSP-capable cameras (Reolink, to be specific) for home security and to play around with the detection capabilities of Frigate and others (like double-take). After using the CPU as my main horse power, the Google Coral TPUs popped up, in specific the small USB accelerator. Ok, bought. But how to use it with Proxmox?

The setup

I wanted to use my existing Proxmox hypervisors for running Frigate in a LXC container. To do so, we need the following:

  1. The Coral TPU plugged into one USB port of your hypervisor
  2. An LXC container (Debian perferred, but that’s up to you) with Docker installed
  3. Some tweaks to get the Coral TPU passed through to the Frigate container inside the LXC container

Prepare Proxmox LXC container

First of all, get the USB device information. To do so, login to Proxmox via SSH and execute lsusb. You should see something similar like this:

$ lsusb
Bus 004 Device 004: ID 1a6e:089a Global Unichip Corp.
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 002: ID 8087:0026 Intel Corp. AX201 Bluetooth
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

In my case, the TPU is listed as Global Unichip Corp. device on USB bus 4. We need to note that. Some may asking: But I thought this is a Google device?. Yes, be aware that the USB device ID and manufactor name is changing after first use of the TPU.

Next, some best practice according to other folks out there: create a udev rule to configure the USB device permissions, to allow non-root users to access the device without additional permissions. Make sure that you’re using the correct vendor and product ID.

$ echo 'SUBSYSTEMS=="usb", ATTRS{idVendor}=="1a6e", ATTRS{idProduct}=="089a", MODE="0664", TAG+="uaccess"' | tee -a /etc/udev/rules.d/71-edgetpu.rules > /dev/null

$ udevadm control --reload-rules && udevadm trigger

Now create a new LXC container, which is privileged and has nesting enabled. Don’t start it yet. After creation, navigate to the container conf file (to be found in /etc/pve/lxc/<id>.conf) and add the following:

unprivileged: 0
lxc.cgroup2.devices.allow: c 226:0 rwm
lxc.cgroup2.devices.allow: c 226:128 rwm
lxc.cgroup2.devices.allow: c 29:0 rwm
lxc.cgroup2.devices.allow: c 189:* rwm
lxc.apparmor.profile: unconfined
lxc.cgroup2.devices.allow: a
lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file 0, 0
lxc.mount.entry: /dev/bus/usb/004 dev/bus/usb/004 none bind,optional,create=dir 0, 0
lxc.cap.drop:
lxc.mount.auto: cgroup:rw

Again, make sure that you use the correct USB bus mount entry (in my case 004).

After that, start the container, install Docker (e.g. using the convenience script: curl -fsSL https://get.docker.com -o get-docker.sh && sh get-docker.sh) as well as the usbutils package to be able to execute lsusb.

Execute lsusb to list all connected USB devices. You should see the Global Unichip Corp. device as well.

Start Frigate

Now we can start Frigate to use the TPU. An example docker-compose file is the following - adapt this to your needs:

version: "3.9"

services:
  frigate:
    container_name: frigate
    privileged: true
    restart: unless-stopped
    image: ghcr.io/blakeblackshear/frigate:0.13.0-beta6
    shm_size: "126.99mb"
    devices:
      - /dev/bus/usb:/dev/bus/usb # passes the USB Coral, needs to be modified for other versions
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - <your-path>/config:/config # config path
      - <your-path>:/media/frigate # recordings path
      - type: tmpfs # Optional: 1GB of memory, reduces SSD/SD Card wear
        target: /tmp/cache
        tmpfs:
          size: 1000000000
    ports:
      - "5000:5000"
      - "8554:8554" # RTSP feeds
      - "8555:8555/tcp" # WebRTC over tcp
      - "8555:8555/udp" # WebRTC over udp
    environment:
      FRIGATE_RTSP_PASSWORD: "asuperlongandsecurepassword"

In the config.yaml inside the config directory, set the Coral TPU detector:

...

detectors:
  coral:
    type: edgetpu
    device: usb

...

Please see the Frigate config documentation for all possible config options.

Start the container: docker compose up -d

After a short time, you should see the container crashing because it’s not detecting any TPU. This is expected since the TPU changed its ID and name after the first use. More information about that here.

We now need to adapt our LXC config.

Adapt Proxmox LXC config

Again, on the Proxmox host, execute lsusb:

$ lsusb
Bus 004 Device 005: ID 18d1:9302 Google Inc.
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 002: ID 8087:0026 Intel Corp. AX201 Bluetooth
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

You see, we’re now dealing with a Google device. Again, execute the following to extend the udev rules:

$ echo 'SUBSYSTEMS=="usb", ATTRS{idVendor}=="18d1", ATTRS{idProduct}=="9302", MODE="0664", TAG+="uaccess"' | tee -a /etc/udev/rules.d/71-edgetpu.rules > /dev/null

$ udevadm control --reload-rules && udevadm trigger

Restart Frigate

Now we can restart the Frigate container: docker compose restart

When looking at the logs (docker compose logs -f), you should now see the following:

...
frigate | frigate.detectors.plugins.edgetpu_tfl INFO    : Attempting to load TPU as usb
frigate | frigate.detectors.plugins.edgetpu_tfl INFO    : TPU found
...

That’s it! You’re ready to use the TPU.

If you like posts like that and want to support me, some sats (see my about page) are highly appreciated 🧡