Created
November 23, 2025 12:41
-
-
Save AnnoyingTechnology/95912b4efefc15db37af1bad3c7b4f37 to your computer and use it in GitHub Desktop.
build-coral-tpu-drivers-for-proxmox-ve-9.sh
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # | |
| # Build Coral TPU kernel module in a throwaway LXC and install it on the host | |
| # WITHOUT DKMS or build deps on the host. | |
| # | |
| set -e | |
| CTID=999 | |
| STORAGE_PATH_BASE="/local-vms" | |
| CT_ROOT="${STORAGE_PATH_BASE}/subvol-${CTID}-disk-0" | |
| TEMPLATE_STORAGE="images" | |
| TEMPLATE_NAME="debian-13-standard_13.1-2_amd64.tar.zst" | |
| ROOTFS_STORAGE="local-vms" | |
| HOST_TPU_DIR="/tmp/tpu" | |
| BRIDGE="vmbr0" | |
| KVER="$(uname -r)" | |
| if [ "$(id -u)" -ne 0 ]; then | |
| echo "Run as root." | |
| exit 1 | |
| fi | |
| echo ">>> Using kernel: ${KVER}" | |
| echo ">>> (Info) CT root would be at: ${CT_ROOT}" | |
| mkdir -p "${HOST_TPU_DIR}" | |
| echo ">>> Cleaning up any old container ${CTID}…" | |
| if pct status "${CTID}" &>/dev/null; then | |
| pct stop "${CTID}" 2>/dev/null || true | |
| pct destroy "${CTID}" --force || true | |
| fi | |
| echo ">>> Creating LXC ${CTID}…" | |
| pct create "${CTID}" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE_NAME}" \ | |
| -hostname tpu-builder \ | |
| -memory 2048 \ | |
| -cores 2 \ | |
| -net0 name=eth0,bridge=${BRIDGE},ip=dhcp \ | |
| -rootfs "${ROOTFS_STORAGE}:8" \ | |
| -features nesting=1 | |
| echo ">>> Starting LXC ${CTID}…" | |
| pct start "${CTID}" | |
| echo ">>> Building inside container…" | |
| pct exec "${CTID}" -- env -i LANG=C LC_ALL=C DEBIAN_FRONTEND=noninteractive bash -lc ' | |
| set -e | |
| apt update | |
| apt install -y \ | |
| curl gnupg git build-essential ca-certificates \ | |
| libdw1 libelf-dev bc flex bison | |
| # Coral repo | |
| 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 | |
| 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 | |
| # Proxmox repo for headers | |
| curl -fsSL https://enterprise.proxmox.com/debian/proxmox-release-trixie.gpg \ | |
| -o /etc/apt/trusted.gpg.d/proxmox-release-trixie.gpg | |
| echo "deb [arch=amd64] http://download.proxmox.com/debian/pve trixie pve-no-subscription" \ | |
| > /etc/apt/sources.list.d/pve.list | |
| apt update | |
| # lib .deb for host | |
| mkdir -p /usr/local/src/coral | |
| cd /usr/local/src/coral | |
| apt-get download libedgetpu1-std | |
| # Headers & build deps INSIDE the CT only | |
| apt install -y "pve-headers-$(uname -r)" | |
| # Build the kernel module | |
| git clone https://github.com/google/gasket-driver.git | |
| cd gasket-driver | |
| git fetch origin pull/50/head:pr-50 | |
| git checkout pr-50 | |
| # Build from src/ where the module Makefile lives | |
| make -C /lib/modules/$(uname -r)/build M="$(pwd)/src" modules | |
| # We expect apex.ko and gasket.ko in src/ | |
| ls -l src/*.ko | |
| ' | |
| echo ">>> Copying artifacts out of CT ${CTID} with pct pull…" | |
| # Find the libedgetpu deb path inside the container | |
| LIB_DEB_IN_CT=$(pct exec "${CTID}" -- bash -lc 'echo /usr/local/src/coral/libedgetpu1-std_*.deb' | tr -d "\r") | |
| if [ -z "${LIB_DEB_IN_CT}" ] || [[ "${LIB_DEB_IN_CT}" == *"*"* ]]; then | |
| echo "ERROR: libedgetpu1-std_*.deb not found inside container" | |
| pct stop "${CTID}" 2>/dev/null || true | |
| pct destroy "${CTID}" --force 2>/dev/null || true | |
| exit 1 | |
| fi | |
| # Pull libedgetpu .deb from CT to host (DESTINATION MUST BE A FILE, NOT JUST A DIR) | |
| LIB_DEB_BASENAME=$(basename "${LIB_DEB_IN_CT}") | |
| pct pull "${CTID}" "${LIB_DEB_IN_CT}" "${HOST_TPU_DIR}/${LIB_DEB_BASENAME}" | |
| # Pull any built .ko modules from src/ in the CT to host | |
| KO_PATHS=$(pct exec "${CTID}" -- bash -lc 'echo /usr/local/src/coral/gasket-driver/src/*.ko 2>/dev/null || true' | tr -d "\r") | |
| if [ -n "${KO_PATHS}" ] && [[ "${KO_PATHS}" != *"*"* ]]; then | |
| for ko in ${KO_PATHS}; do | |
| KO_BASENAME=$(basename "${ko}") | |
| pct pull "${CTID}" "${ko}" "${HOST_TPU_DIR}/${KO_BASENAME}" || true | |
| done | |
| else | |
| echo ">>> WARNING: No .ko files found to pull (build may have failed?)" | |
| fi | |
| echo ">>> Stopping CT ${CTID}…" | |
| pct stop "${CTID}" || true | |
| echo ">>> Destroying CT ${CTID}…" | |
| pct destroy "${CTID}" --force || true | |
| echo ">>> Installing artifacts on host from ${HOST_TPU_DIR}…" | |
| cd "${HOST_TPU_DIR}" | |
| echo "Contents of ${HOST_TPU_DIR}:" | |
| ls -l | |
| LIB_DEB=$(ls libedgetpu1-std_*.deb 2>/dev/null || true) | |
| if [ -z "${LIB_DEB}" ]; then | |
| echo "ERROR: libedgetpu1-std_*.deb not found in ${HOST_TPU_DIR}" | |
| exit 1 | |
| fi | |
| # Install lib on host (this is the only host package change) | |
| dpkg -i "${LIB_DEB}" | |
| # Install kernel modules | |
| mkdir -p "/lib/modules/${KVER}/extra" | |
| for ko in *.ko; do | |
| [ -f "$ko" ] || continue | |
| echo ">>> Installing module $ko" | |
| cp "$ko" "/lib/modules/${KVER}/extra/" | |
| done | |
| echo ">>> Running depmod…" | |
| depmod "${KVER}" | |
| echo ">>> Trying to load modules…" | |
| if ! modprobe gasket; then | |
| echo "WARNING: modprobe gasket failed, check dmesg for details." | |
| fi | |
| if ! modprobe apex; then | |
| echo "WARNING: modprobe apex failed, check dmesg for details." | |
| fi | |
| echo ">>> Done." | |
| echo " - Artifacts live in ${HOST_TPU_DIR}" | |
| echo " - libedgetpu is installed; gasket/apex modules installed for ${KVER}" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment