709 字
4 分钟
TrueNAS Build Nvidia vGPU Driver extensions (systemd-sysext)
2025-06-27

A lot of folks are opting to run TrueNAS on Proxmox VE these days. Why? Well, TrueNAS’s built-in VM management can be a bit clunky – even with Incus, the underlying tech isn’t quite seamless, and you often hit snags with CLI commands acting up in the Instance UI. To get around this and supercharge things like RDP desktops or video encoding, many are cleverly using vGPU passthrough to share a single GPU across several virtual machines.

However, TrueNAS by default uses the standard Nvidia drivers, not the specialized Grid drivers (NVIDIA’s commercial drivers). While some Tesla cards might correctly load nvidia-smi, they often can’t actually use their encoding and decoding units with the default drivers.

The good news is that starting with TrueNAS SCALE 24.10+, TrueNAS now uses systemd-sysext to load Nvidia drivers that are pre-packaged during compilation. These extensions are only loaded when needed.


How It Works#

The core principle behind the packaged extension source code is quite straightforward:

  1. It compares the contents of self.chroot and self.chroot_base directories.
  2. It extracts any new or modified files.
  3. Only files under usr/ (excluding usr/src/) are kept; others are deleted.
  4. It cleans up self.chroot by removing files and empty directories that don’t belong to the extension.
  5. An extension-release.d file is added to identify the extension.
  6. Finally, self.chroot is packaged into a SquashFS file and saved to dst_path.

Here’s the Python code:

def build_extension(self, name, dst_path):
changed_files = [
os.path.relpath(filename, self.chroot)
for filename in map(
lambda filename: os.path.join(os.getcwd(), filename),
run(
["rsync", "-avn", "--out-format=%f", f"{self.chroot}/", f"{self.chroot_base}/"],
log=False,
).stdout.split("\n")
)
if os.path.abspath(filename).startswith(os.path.abspath(self.chroot))
]
sysext_files = [f for f in changed_files if f.startswith("usr/") and not (f.startswith("usr/src/"))]
for root, dirs, files in os.walk(self.chroot, topdown=False):
for f in files:
path = os.path.relpath(os.path.abspath(os.path.join(root, f)), self.chroot)
if path not in sysext_files:
os.unlink(os.path.join(root, f))
for d in dirs:
try:
os.rmdir(os.path.join(root, d))
except NotADirectoryError:
os.unlink(os.path.join(root, d)) # It's a symlink
except OSError as e:
if e.errno == errno.ENOTEMPTY:
pass
else:
raise
os.makedirs(f"{self.chroot}/usr/lib/extension-release.d", exist_ok=True)
with open(f"{self.chroot}/usr/lib/extension-release.d/extension-release.{name}", "w") as f:
f.write("ID=_any\n")
run(["mksquashfs", self.chroot, dst_path, "-comp", "xz"])

Specifically for the Nvidia driver, this is the relevant part:

def download_nvidia_driver(self):
prefix = "https://us.download.nvidia.com/XFree86/Linux-x86_64"
version = get_manifest()["extensions"]["nvidia"]["current"]
filename = f"NVIDIA-Linux-x86_64-{version}-no-compat32.run"
result = f"{self.chroot}/{filename}"
self.run(["wget", "-c", "-O", f"/{filename}", f"{prefix}/{version}/{filename}"])
os.chmod(result, 0o755)
return result
def install_nvidia_driver(self, kernel_version):
driver = self.download_nvidia_driver()
self.run([f"/{os.path.basename(driver)}", "--skip-module-load", "--silent", f"--kernel-name={kernel_version}",
"--allow-installation-with-running-driver", "--no-rebuild-initramfs"])
os.unlink(driver)

So, modifying this is actually quite simple:

  • Replace the download link.
  • Adjust the execution options (Grid drivers have different options).

PS: When choosing drivers, pay attention to kernel compatibility. For instance, with the current 25.04 version (kernel version 6.12.15), if you’re using a 16.x driver (the last usable driver for Tesla P4), your grid driver version needs to be greater than 16.9.

Here’s how the modified code would look:

def download_nvidia_driver(self):
# i don't where i can find the grid driver
prefix = "<put your grid download url without filename in here>"
#useless
#version = get_manifest()["extensions"]["nvidia"]["current"]
filename = f"<filename of your grid driver>"
result = f"{self.chroot}/{filename}"
self.run(["wget", "-c", "-O", f"/{filename}", f"{prefix}/{filename}"])
os.chmod(result, 0o755)
return result
def install_nvidia_driver(self, kernel_version):
driver = self.download_nvidia_driver()
# fix option
self.run([f"/{os.path.basename(driver)}", "--silent", f"--kernel-name={kernel_version}"])
os.unlink(driver)

Building the Extension#

In here, I’ll use 25.04.0 as an example.

The official documentation already provides some hints, but here are some key things to note:

Install Dependencies#

(Nothing need to notice)

Terminal window
sudo apt install build-essential debootstrap git python3-pip python3-venv squashfs-tools unzip libjson-perl rsync libarchive-tools

Select the Tags Branch#

Use the tag branch instead of release/xxxxx

Terminal window
# tag name is TS-<version>
git clone -b TS-25.04.0 https://github.com/truenas/scale-build.git

Configure Environment Variables#

The code reads environment variables to determine the compiled “version” and “Train”. So, if you don’t specify them, you’ll only be able to compile the Master train.

Terminal window
export TRUENAS_TRAIN="TrueNAS-SCALE-Fangtooth"
export TRUENAS_VERSION="25.04.0"

Start the Build#

Terminal window
make checkout
make packages
make update

Once the build is complete, you’ll need to mount the compiled rootfs.squashfs to extract its contents. Here’s how:

Terminal window
mkdir -p ./tmpfile/rootfs
mount ./tmp/update/rootfs.squashfs ./tmpfile/rootfs

At this point, your nvidia.raw extension should be available:

Terminal window
ls -al ./tmpfile/rootfs/usr/share/truenas/sysext-extensions/nvidia.raw

Overwriting the Existing Driver#

You’ll need to replace the nvidia.raw file on your running TrueNAS system at /usr/share/truenas/sysext-extensions/nvidia.raw with the one you just compiled.

First, you need to make the /usr dataset writable:

Terminal window
zfs set readonly=off boot-pool/ROOT/<system version>/usr
# For 25.04.0, it would be:
zfs set readonly=off boot-pool/ROOT/25.04.0/usr

PS: If you’ve previously enabled the Nvidia driver via the WebUI, you might need to run systemd-sysext unmerge first. This is because systemd-sysext otherwise controls /usr as read-only.

Overwrite it!

cp /the-path-you-upload/nvidia.raw /usr/share/truenas/sysext-extensions/nvidia.raw

After you’ve copied the file, simply run:

Terminal window
systemd-sysext merge
# Don't forget to restart docker
systemctl restart docker

And, It should work.

nvidia-smi
TrueNAS Build Nvidia vGPU Driver extensions (systemd-sysext)
https://www.homelabproject.cc/posts/truenas/truenas-build-nvidia-vgpu-driver-extensions-systemd-sysext/
作者
Channing He
发布于
2025-06-27
许可协议
CC BY-NC-SA 4.0