Quick start — Proxmox LXC
A Proxmox LXC is the primary deployment shape for hal0 on AMD Strix Halo. An LXC shares the host kernel, so the iGPU and XDNA NPU are passed through as ordinary device nodes — no VM, no vGPU shim, near-native throughput. hal0’s own FLM provider treats “Strix Halo under Proxmox LXC” as its main target.
This guide assumes a working Proxmox VE host with a Strix Halo (Ryzen AI Max+) APU and walks the full path: prepare the host kernel, create a privileged Ubuntu 24.04 container, pass through the GPU + NPU, install hal0, and wire up the NPU trio.
1. Prepare the Proxmox host
Section titled “1. Prepare the Proxmox host”Everything the accelerators need lives on the host kernel, because the LXC shares it. Do this once on the PVE host.
Drivers and firmware
Section titled “Drivers and firmware”The iGPU uses the in-tree amdgpu driver. The NPU needs the amdxdna
kernel module loaded and recent NPU firmware:
- Kernel ≥ 6.11 with
amdxdnaavailable (modprobe amdxdna; confirm/dev/accel/accel0appears on the host). - NPU firmware ≥ 1.1.0.0 (validated in the field at 1.1.2.65).
hal0 supplies the userspace runtime (XRT + the FastFlowLM binary) inside its container image, but it cannot load a kernel driver from inside a container — the driver and firmware must already be present on the host.
Size the GTT pool
Section titled “Size the GTT pool”On Strix Halo the GPU’s usable memory is the amdgpu GTT pool, which is carved from system RAM (unified memory). A model can only allocate up to the GTT cap, not the full RAM pool — so if GTT is left at its small default, large models won’t fit even on a 128 GB box.
hal0 measures the live GTT cap and sizes its model recommendations against it, but it does not configure GTT for you. Set it on the host kernel command line. On a 128 GB Strix Halo, the reference values from hal0’s profile-tuning work are:
# /etc/default/grub — append to GRUB_CMDLINE_LINUX_DEFAULT, then update-grub && rebootamd_iommu=off amdgpu.gttsize=126976 ttm.pages_limit=32505856| Parameter | Value | Meaning |
|---|---|---|
amdgpu.gttsize | 126976 | GTT window in MiB (≈ 124 GiB) |
ttm.pages_limit | 32505856 | TTM page cap in 4 KiB pages (≈ 124 GiB — keep it equal to gttsize) |
amd_iommu=off | — | Disables the IOMMU; measured worth ~5–12% on this workload |
2. Create the container
Section titled “2. Create the container”Create a privileged Ubuntu 24.04 LXC with nesting enabled (containers run inside it) and enough disk for models:
# on the Proxmox host, as rootpct create 105 local:vztmpl/ubuntu-24.04-standard_24.04-2_amd64.tar.zst \ --hostname hal0 --cores 8 --memory 8192 --swap 2048 \ --rootfs local-zfs:64 --net0 name=eth0,bridge=vmbr0,ip=dhcp \ --ostype ubuntu --features nesting=1,fuse=1,keyctl=1,mknod=1 \ --unprivileged 0 --onboot 13. Pass through the GPU and NPU
Section titled “3. Pass through the GPU and NPU”Append the passthrough block to /etc/pve/lxc/105.conf on the host. This
exposes four device nodes and grants the container cgroup permission to
open them:
dev0: /dev/dri/renderD128dev1: /dev/dri/card0dev2: /dev/kfddev3: /dev/accel/accel0lxc.cgroup2.devices.allow: c 226:* rwmlxc.cgroup2.devices.allow: c 234:* rwmlxc.cgroup2.devices.allow: c 261:* rwmlxc.cgroup2.devices.allow: c 10:200 rwmlxc.prlimit.memlock: unlimitedlxc.apparmor.profile: unconfined| Device | Node | Role |
|---|---|---|
| iGPU render | /dev/dri/renderD128 | Vulkan / ROCm render node |
| iGPU card | /dev/dri/card0 (amdgpu) | DRM card device |
| ROCm compute | /dev/kfd | ROCm compute queues |
| XDNA NPU | /dev/accel/accel0 | FastFlowLM NPU device |
The cgroup majors correspond to the DRM (226), the dynamically-allocated
kfd/accel majors (234, 261), and a misc device (10:200) on this
reference host. lxc.prlimit.memlock: unlimited lets the NPU DMA-lock
model weights; lxc.apparmor.profile: unconfined is required for the
container runtime to launch slots inside the LXC.
Start (or restart) the container so the passthrough takes effect:
pct start 1054. Install hal0 in the container
Section titled “4. Install hal0 in the container”Open a shell in the container (pct enter 105) and install the one
prerequisite the Ubuntu base image lacks, then run the installer:
apt-get update && apt-get install -y python3-venv python3-pipcurl -fsSL https://hal0.dev/install.sh | bashThe installer auto-installs podman, builds the Python venv, writes the
systemd units, probes the hardware (it should now see the iGPU and NPU
via the passed-through nodes), seeds a hardware-recommended slot, and
starts hal0-api on port 8080. See Install hal0
for the full breakdown of what the bootstrap does.
5. Set up the NPU trio (FastFlowLM)
Section titled “5. Set up the NPU trio (FastFlowLM)”On an apt host with the NPU present, the installer already handles the
FastFlowLM (FLM) userspace: it verifies and installs the pinned
FastFlowLM .deb (matched to your Ubuntu release), pulls the NPU runtime
libraries, and installs libxrt-npu2 from the lemonade PPA. A single
flm serve process backs the NPU trio — one chat model plus optional
embedding and speech-to-text “passengers” sharing the NPU.
Confirm the NPU runtime is reachable from inside the container:
flm validate # → "flm validate ok — NPU runtime reachable"hal0 doctor # re-runs preflight; hardware probe records the NPUThe seeded npu slot serves a chat model on port 8088. The trio’s
embedding and STT passengers are opt-in — enable them per slot (the
asr / embed keys on the slot’s [npu] table) once the chat slot is
loaded. See Voice (STT/TTS) and
Slots for the trio routing details.
6. Surface true host memory pressure (optional)
Section titled “6. Surface true host memory pressure (optional)”Inside an LXC, hal0 only sees its own cgroup memory share — but the GPU’s GTT pool is drawn from the same physical DIMMs every other tenant and the host kernel use. Give hal0 a read-only Proxmox API token so the dashboard’s unified-memory bar shows real host pressure instead of pretending only this container’s bytes exist.
Create a read-only token in the Proxmox UI (Datacenter → Permissions →
API Tokens, with PVEAuditor on /), then configure it via the dashboard
Settings → Proxmox card, or write /etc/hal0/proxmox.json directly:
{ "proxmox": { "host": "192.0.2.10", "port": 8006, "verify_ssl": false, "service": "PVE" }, "auth": { "user": "hal0@pve", "token_name": "readonly", "token_value": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" }}hal0 polls GET /cluster/resources (cached 30 s) and surfaces host RAM
total/used plus every running tenant’s allocation. Non-Proxmox installs
leave the file absent and the feature stays silent.
7. Verify
Section titled “7. Verify”hal0 status # system + slot statussystemctl status hal0-api # control planejournalctl -fu hal0-api # live logsThe API and dashboard listen on http://<container-ip>:8080; OpenWebUI on
:3001.