Targets an Intel Core Ultra (Meteor Lake / Arrow Lake / Lunar Lake) class host with an Arc Xe iGPU and an AI Boost NPU. Both feed the node_exporter textfile collector (no privileged container); a systemd timer runs them every 15s.
Requires intel-gpu-tools (apt install intel-gpu-tools). Samples intel_gpu_top -J briefly and
emits:
intel_gpu_engine_busy_ratio{engine}— per engine (Render/3D, Blitter, Video, VideoEnhance, Compute)intel_gpu_frequency_mhz{type}— requested/actualintel_gpu_power_watts{domain}— GPU/Packageintel_gpu_memory_bytes{type=resident|total}— iGPU memory (summed per-client; integrated GPUs use shared system RAM, so this is also charged against total RAM in the Host memory breakdown)intel_gpu_clients,intel_gpu_rc6_ratio,intel_gpu_present
Safe no-op if the binary or device is absent (intel_gpu_present 0).
On this class of SoC the iGPU and NPU share system RAM. The Host dashboard shows
intel_gpu_memory_bytes and intel_npu_memory_bytes as bytes and % of node_memory_MemTotal_bytes,
plus a stacked "where's my RAM" bar (processes vs iGPU vs NPU vs free). Use ignoring(type) /
scalar() when dividing the GPU metric (it carries a type label) by node_memory_*.
Pure sysfs (the intel_vpu driver), zero dependencies. Locates the NPU PCI device dynamically and
emits:
intel_npu_busy_time_us(counter → utilization viarate())intel_npu_frequency_mhz{type=current|max}intel_npu_memory_bytes,intel_npu_runtime_active_ms,intel_npu_present
sudo apt install -y intel-gpu-tools
sudo cp systemd/accel-textfile.{service,timer} /etc/systemd/system/
sudo systemctl enable --now accel-textfile.timerThe node_exporter container mounts the textfile dir read-only; the scripts write world-readable
.prom files there.
If the host is a hypervisor, add a host exporter via a private compose overlay
(docker-compose.override.yml, git-ignored) and a scrape job — kept out of this repo to stay generic.
For the RTX 3090 eGPU (CUDA inference host). Wraps nvidia-smi (driver on the host), LC_ALL=C so
awk emits dot decimals, atomic write, safe no-op if nvidia-smi/the card is absent. Chained from
accel_collect.sh (same accel-textfile.timer). Emits (label gpu,name):
nvidia_gpu_utilization_ratio(0–1),nvidia_gpu_temperature_celsius,nvidia_gpu_power_watts/nvidia_gpu_power_limit_watts,nvidia_gpu_clock_sm_mhz/_mem_mhznvidia_gpu_memory_{used,free,total}_bytesnvidia_gpu_process_memory_bytes{pid,process}— per-process VRAM (e.g. thellama-serverinference process). NB: QMD's embeddings run on the Intel iGPU (Vulkan), not the 3090, so they don't appear here.nvidia_gpu_present(1/0) Surfaced on the Accelerators dashboard (NVIDIA RTX 3090 row). Alerts:NvidiaVRAMHigh(>95%, OOM risk),NvidiaGPUHot(>85°C),NvidiaGPUSaturated.
The RTX 3090 is served by llama-swap (ghcr.io/mostlygeek/llama-swap:cuda), an on-demand loader that
replaced the always-on llama-arc container (2026-07-01). It exposes llamaswap_* system + GPU gauges at
:8080/metrics. Prometheus scrapes it directly over the shared litellm_default network (job
llama-swap). The LLM Inference — llama.cpp + MTP dashboard (ai-agents) shows GPU
util/power/temp/VRAM/fan + host load. Because models load on demand and unload after 900s idle, these read
near-zero when nothing is loaded (expected, not an outage). Per-token llamacpp:* decode/prefill/MTP
metrics are not scraped: they live on the upstream llama-server behind /upstream/<model> and
scraping them would trigger a model load every interval and defeat the idle TTL. Alert: LlamaSwapDown.
Firewall requirement (important): node-exporter runs network_mode: host, and the dockerised
Prometheus scrapes it via host.docker.internal:9100. The host's INPUT policy is DROP, so
setup-firewall.sh must allow it:
iptables -A INPUT -s 172.16.0.0/12 -p tcp --dport 9100 -j ACCEPT.
Without that rule the entire node job is down (no CPU/RAM/disk/iGPU/NPU/NVIDIA metrics) — the
scrape times out (context deadline exceeded).