Install Coral TPU on the Host

Hardware: GPUs and Coral-TPU~10 minView script

Prepare the Proxmox host so a Coral TPU can later be passed to an LXC container. ProxMenux auto-detects whether you have a M.2 / Mini-PCIe card, a USB Accelerator, or both, and runs only the install path that applies — kernel-module build via DKMS for PCIe, Google's Edge TPU runtime for USB.

What this does

The Coral installer is unified and hardware-aware: you run one menu option and it figures out what you actually have. M.2 / Mini-PCIe cards need kernel modules (gasket + apex) built with DKMS so they survive kernel upgrades. USB Accelerators just need the Google Edge TPU runtime (libedgetpu1-std) from Google's own APT repository. Both types plugged in? Both paths run in sequence.

Which Coral do I have?

The two kinds of Coral devices you can plug into a Proxmox host look very different and the host-side install is very different too. The script handles both, but it's good to know what you have before running anything:

Form factorDetectionInstall typeReboot?
M.2 / Mini-PCIe
(dual-edge TPU on an M.2 card)
PCI vendor 1ac1Kernel module (gasket + apex) via DKMSYes
USB Accelerator
(small plastic dongle)
USB 1a6e:089a (unprogrammed) / 18d1:9302 (programmed)User-space runtime (libedgetpu1-std) from Google APT repoNo

Before you start

  • A Coral TPU plugged into the host (M.2, Mini-PCIe or USB). No Coral means no install — the script just tells you nothing was detected and exits.
    lspci -d 1ac1: ; lsusb | grep -E '1a6e:089a|18d1:9302'
  • Internet access on the host — for PCIe the DKMS path clones a GitHub repo, for USB it adds a Google APT repo and downloads the libedgetpu1-std package.
  • Kernel headers available via APT. On Proxmox this means proxmox-headers-$(uname -r) must be installable — needed only for the PCIe/M.2 path, but the script fetches it automatically so you don't have to.
  • Be OK with a reboot if you have PCIe hardware. The kernel module is built and loaded at the end, but a fresh boot is the clean way to confirm it comes up on its own.

This only prepares the host

Installing Coral here makes the host ready — the TPU is visible to the host kernel and can be handed to an LXC. To actually use it from a container (Frigate, DeepStack, etc.), the next step is Add Coral TPU to an LXC.

Running the installer

Open ProxMenux on the host, go to Hardware: GPUs and Coral-TPU → Install/Update Coral TPU on Host.

Menu entry for 'Install/Update Coral TPU on Host' inside Hardware: GPUs and Coral-TPU

How the script runs

Hardware is detected first, so the install plan is shaped to your actual setup. Nothing is installed until you confirm.

┌────────────────────────────────────────────────┐
│ 1. detect_coral_hardware()                     │
│    → count PCIe (vendor 1ac1) + USB (IDs)      │
└────────────────┬───────────────────────────────┘
                 ▼
     ┌───────────┴───────────┐
     │                       │
     ▼                       ▼
  None                 At least one
     │                       │
     ▼                       ▼
 Dialog            pre_install_prompt()
 "No Coral" →      shows what was detected
  exit 0           and what will be installed
                           │
                           ▼
          ┌────────────────┴────────────────┐
          │                                 │
          ▼                                 ▼
   PCIe detected?                    USB detected?
          │                                 │
        Yes                               Yes
          ▼                                 ▼
 install_gasket_apex_dkms        install_libedgetpu_runtime
 ├─ cleanup_broken_gasket_dkms   ├─ add Google GPG keyring
 ├─ apt install deps             │    /etc/apt/keyrings/...
 │  (git, dkms, build-essential, ├─ add APT repo (signed-by)
 │   proxmox-headers-$(uname-r)) │    /etc/apt/sources.list.d/
 ├─ clone feranick/gasket-driver │     coral-edgetpu.list
 │   (google fallback + patches) ├─ apt install libedgetpu1-std
 ├─ copy src/ → /usr/src/        └─ udev reload + trigger
 │   gasket-1.0/
 ├─ generate dkms.conf
 ├─ dkms add / build / install
 └─ modprobe gasket + apex
 + ensure_apex_group_and_udev
          │                                 │
          └────────────────┬────────────────┘
                           ▼
          ┌────────────────┴────────────────┐
          │                                 │
       PCIe ran?                       USB only
          │                                 │
          ▼                                 ▼
  restart_prompt()          "No reboot required"
  (reboot required to       (runtime + udev rules
   load fresh kernel         are already active)
   module cleanly)

Walking through the flow

Step 1

Hardware detection

The script reads /sys/bus/pci/devices/*/vendor looking for 0x1ac1 (Global Unichip Corp., the silicon vendor of the Coral TPU chips) and runs lsusb looking for the USB Accelerator IDs. The USB device has two IDs depending on whether its firmware has been loaded yet:

  • 1a6e:089a — Global Unichip, unprogrammed state (before the Edge TPU runtime talks to it the first time).
  • 18d1:9302 — Google, programmed state (after the runtime loads firmware onto it).

If neither is found, you get an informative dialog and the script exits cleanly — no partial changes on the host.

Step 2

Pre-install prompt

Before touching anything, a single dialog summarises what was detected and what will be installed. You can cancel here without any side effects.

Pre-install dialog showing detected Coral hardware (M.2/PCIe count + USB count) and the list of steps the installer will run
Step 3

PCIe path — gasket + apex kernel modules via DKMS

Only runs if the script found a PCIe / M.2 Coral. It's the heavier of the two paths because it's compiling a kernel module for your exact running kernel.

  1. Cleanup. If a previous gasket-dkms install left dpkg in a broken state (typical after a PVE 9 kernel upgrade where DKMS autoinstall failed silently), force-purge it.
  2. Install build deps: git, dkms, build-essential, proxmox-headers-$(uname -r).
  3. Clone the driver source. Prefers the feranick/gasket-driver community fork (actively maintained, kernel 6.12+ ready). Falls back to google/gasket-driver if feranick is unreachable, applying kernel-specific patches:
    • Kernel 6.5+ : no_llseek removed upstream → noop_llseek substitution
    • Kernel 6.13+ : MODULE_IMPORT_NS(DMA_BUF) requires string literal
  4. Stage sources under /usr/src/gasket-1.0/, generate dkms.conf, register with dkms add.
  5. Build + install: dkms build then dkms install. If anything fails, the last 50 lines of make.log print to the terminal so you see the real error — no hunting in log files.
  6. Create the apex group + udev rules (/etc/udev/rules.d/99-coral-apex.rules) so /dev/apex_* nodes get the right group on the next boot.
  7. Load the modules: modprobe gasket + modprobe apex.
DKMS build progress + module load output for the gasket/apex kernel modules
Step 4

USB path — Edge TPU runtime from Google

Only runs if a USB Accelerator was detected. Much simpler:

  1. Add Google's GPG key to /etc/apt/keyrings/coral-edgetpu.gpg.
  2. Add the APT repository to /etc/apt/sources.list.d/coral-edgetpu.list with signed-by= pointing at the keyring (modern, non-deprecated format).
  3. Install libedgetpu1-std — the standard-performance Edge TPU runtime.
  4. Reload udev so the rules shipped with the package apply to the USB device without having to unplug/replug.

Why libedgetpu1-std and not libedgetpu1-max?

The max variant overclocks the Coral and runs hotter. Fine for desktop use with airflow; not recommended inside small NUC / Mini-PC builds or passively cooled Proxmox hosts. If you really want it, install by hand afterwards: apt install libedgetpu1-max.
Step 5

Reboot prompt (only if PCIe ran)

If the PCIe path ran, the script offers a reboot. The modules were modprobe'd already so in theory you can skip the reboot — but a clean boot is the right way to verify the module comes up on its own and /dev/apex_0 appears with group apex. For USB-only installs, no reboot is suggested (the runtime and udev rules are active immediately).

Final summary + reboot prompt after a PCIe install

Reinstall or uninstall

Running the installer on a host where Coral is already installed (PCIe via gasket-dkms, USB via libedgetpu1-std/libedgetpu1-max, or both) no longer drops straight into another fresh install. Instead, ProxMenux detects the existing setup and shows an action menu so you can decide what to do.

Coral action menu with two choices — Reinstall / update Coral drivers, or Uninstall Coral drivers and configuration — shown when the installer detects a previous Coral install on the host
The action menu only appears when at least one Coral component is already installed (DKMS gasket entry, libedgetpu1-* package, or live /dev/apex_* device nodes).

Reinstall / update

Continues with the normal install flow — useful after a kernel upgrade if the DKMS rebuild didn't happen automatically, or to lift the runtime to a newer libedgetpu1-* from the Google Coral apt repo. The previous DKMS state is cleaned up first so a half-installed package from a failed earlier attempt doesn't block the new build.

Uninstall — what gets removed

Confirms with a yes/no dialog before doing anything (LXC containers with apex passthrough will lose access to /dev/apex_* after the next reboot — the warning makes that clear). Then it performs a full, idempotent rollback:

  • Unloads the apex and gasket kernel modules.
  • Removes every DKMS-registered gasket/<version> entry so the modules don't come back on the next kernel install.
  • Purges the gasket-dkms, libedgetpu1-std and libedgetpu1-max apt packages, then runs apt-get autoremove --purge.
  • Deletes the udev rule /etc/udev/rules.d/99-coral-apex.rules ProxMenux wrote; restores the upstream 60-gasket-dkms.rules group to its default if it's still present.
  • Removes the apex system group only if no users still belong to it — if you mapped a custom user into apex for an LXC passthrough, the group is left in place and a warning prints the current members.
  • Cleans the Google Coral apt repository entry and keyring (/etc/apt/sources.list.d/coral-edgetpu.list + coral-edgetpu-archive-keyring.gpg).
  • Prompts for a reboot at the end only if the PCIe path was installed — the cleanest way to flush the kernel modules. USB-only uninstalls don't need one.

LXC containers with apex passthrough

Uninstalling on the host invalidates the device path mapped into any LXC container configured for apex passthrough. Plan the operation during a maintenance window if Frigate / DeepStack / similar workloads depend on it.

Update notifications

ProxMenux now tracks the installed Coral components in its managed-installs registry. Both variants are followed independently — a host with both M.2 and USB Coral devices gets two update streams, each with its own upstream source:

VariantTracked versionUpstream source
PCIe / M.2gasket-dkms Debian version (or the DKMS-registered version if the package was force-removed)Latest tag of feranick/gasket-driver on GitHub (7-day cache, build-number comparison)
USBlibedgetpu1-std or libedgetpu1-max apt versionLocal apt-cache policy candidate (Google Coral apt repo)

When a newer version is detected the Monitor fires a coral_driver_update_available notification on every enabled channel (Telegram, Discord, Gotify, ntfy, email, webhook). The notification points back at the same installer entry — pick Reinstall / update from the action menu above to apply it.

Anti-cascade by design

One notification per variant, only when the upstream version actually changes — never on every 24h scan. If you ignore an update it doesn't re-ping you until a newer release lands.

Reboot is only needed for the PCIe path

The gasket DKMS rebuild loads new kernel modules — that needs a reboot to be active. The USB runtime upgrade is a user-space library swap, no reboot required.

Manual equivalent

If you want to know what happens under the hood, or redo an individual step by hand, the raw commands per path look like this.

PCIe / M.2 (gasket + apex via DKMS)

# Build deps
apt-get update
apt-get install -y git dkms build-essential "proxmox-headers-$(uname -r)"

# Clone the (maintained) fork
cd /tmp
rm -rf gasket-driver
git clone --depth=1 https://github.com/feranick/gasket-driver.git

# Stage under /usr/src for DKMS
cp -a /tmp/gasket-driver/src/. /usr/src/gasket-1.0/

# Tell DKMS what it is
cat > /usr/src/gasket-1.0/dkms.conf <<'EOF'
PACKAGE_NAME="gasket"
PACKAGE_VERSION="1.0"
BUILT_MODULE_NAME[0]="gasket"
BUILT_MODULE_NAME[1]="apex"
DEST_MODULE_LOCATION[0]="/updates/dkms"
DEST_MODULE_LOCATION[1]="/updates/dkms"
MAKE[0]="make KVERSION=${kernelver}"
CLEAN="make clean"
AUTOINSTALL="yes"
EOF

# Register + build + install
dkms add    /usr/src/gasket-1.0
dkms build  gasket/1.0 -k "$(uname -r)"
dkms install gasket/1.0 -k "$(uname -r)"

# Load it
modprobe gasket
modprobe apex

# Group + udev so /dev/apex_* end up with sane perms
groupadd --system apex 2>/dev/null || true
cat > /etc/udev/rules.d/99-coral-apex.rules <<'EOF'
KERNEL=="apex_*",   GROUP="apex", MODE="0660"
SUBSYSTEM=="apex",  GROUP="apex", MODE="0660"
EOF
udevadm control --reload-rules
udevadm trigger --subsystem-match=apex || true

# (Reboot recommended)

USB (libedgetpu runtime)

# GPG key + repo (modern signed-by layout)
mkdir -p /etc/apt/keyrings
curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg \
  | gpg --dearmor -o /etc/apt/keyrings/coral-edgetpu.gpg
chmod 0644 /etc/apt/keyrings/coral-edgetpu.gpg

echo 'deb [signed-by=/etc/apt/keyrings/coral-edgetpu.gpg] https://packages.cloud.google.com/apt coral-edgetpu-stable main' \
  > /etc/apt/sources.list.d/coral-edgetpu.list

# Install the runtime
apt-get update
apt-get install -y libedgetpu1-std

# Reload udev so shipped rules apply to anything already plugged in
udevadm control --reload-rules
udevadm trigger --subsystem-match=usb || true

Verification

PCIe / M.2

# Module loaded?
lsmod | grep apex
# Expect: apex, gasket (gasket as dependency)

# Device node present with apex group?
ls -l /dev/apex_*
# Expect: crw-rw---- 1 root apex ...  /dev/apex_0

# Kernel sees the device cleanly?
dmesg | grep -i apex | tail -10
# Expect: "Apex chip ID ..." / "apex 0000:xx:00.0: Apex performance changed to ..."

USB

# Is it seen by USB?
lsusb | grep -E '1a6e:089a|18d1:9302'

# Runtime installed?
dpkg -l libedgetpu1-std | grep ii

# Quick smoke test — run the Coral classification example from any Python env
#   pip install pycoral tflite_runtime pillow
# (Ran from the container that will use the TPU, not the host.)

Troubleshooting

DKMS build fails after a kernel upgrade

Most common cause on PVE 9: the running kernel bumped but proxmox-headers-$(uname -r) isn't installed for it yet. Check with dpkg -l proxmox-headers-$(uname -r). Install the missing headers and re-run the script — ProxMenux's cleanup_broken_gasket_dkms step handles any leftover half-configured package state.

/dev/apex_0 missing after reboot

The module isn't loaded. Try modprobe apex by hand. If that errors, check dmesg | grep -iE "apex|gasket" for the real failure — common culprits are kernel version mismatch (DKMS was built for a different kernel than the one you booted) or a firmware upgrade that disabled the PCIe slot the Coral is in.

Can see /dev/apex_0 but LXC can't

Host is fine. Problem is passthrough config — see Add Coral TPU to an LXC.

USB Accelerator detected but Frigate / TFLite can't reach it

Check the udev rules shipped with libedgetpu1-std took effect: ls -l /dev/bus/usb/*/*. The device should NOT be owned by root:root with mode 0600 — if it is, run udevadm control --reload-rules &amp;&amp; udevadm trigger on the host, unplug the USB Coral, wait 3 seconds and plug it back in.

Install log

Every run writes to /tmp/coral_install.log on the host. If the DKMS build dies, the script also appends the last 50 lines of /var/lib/dkms/gasket/1.0/build/make.log to that log — attach it when asking for help on GitHub.

Related