Architecture
Design in one paragraph
llama.cpp built from current source with two backends —
Vulkan (RADV) and HIP (ROCm gfx1151) — benchmarked
head-to-head, winner per workload serves. Model weights live on the dedicated 2TB NVMe
(/srv/llm/models). Multiple models serve side-by-side where memory allows
(Qwen3.8-27B at 19 GB + DeepSeek-V4-Flash at 103 GB ≈ 121 GB co-resident; the 27B + a
smaller endpoint otherwise), coordinated by llama-swap — the same
tier/eviction design proven on our ashborn-prime node. One OpenAI-compatible endpoint
(:8080) fronts everything; a small embeddings model gets a dedicated endpoint; the CPU
reranker pattern carries over verbatim. Everything runs as user wg via
systemd user units so it survives the dan account removal.
Why two backends
- HIP/ROCm — full-speed prefill (prompt processing) and the path with the most Strix Halo community tuning (draft-MTP, the top-k radix fix for Qwen3.8 — both included in today's source build).
- Vulkan/RADV — 2026 consensus: faster decode on Strix Halo than HIP for MoE models (~40% gap in llama.cpp issue #24438), with MTP spec-decode support merged. Built from the same source tree.
- Benchmarks (see Benchmarks) decide which one each model runs on; both binaries stay installed.
Strix Halo tuning (AMD playbook findings)
| Lever | Setting | Status |
|---|---|---|
| GPU-accessible memory | amdgpu.gttsize=126976 ttm.pages_limit=32505856 (124 GiB pair) — default ~50% cap is what breaks 100 GB models | applied, active at next boot |
| BIOS UMA Frame Buffer | 512 MB, explicitly not AUTO (AUTO fights the GTT strategy; capacity-only, no speed effect) | owner action — BIOS menu, hands-on |
| BIOS version | 3.06 (Jul 2026) available via fwupd — fixes a UMA-cap bug, slow-GRUB bug, real CVEs; downgrade supported | owner decision — reboot flash |
| HIP build | -DGGML_HIP_NO_VMM=ON is mandatory on gfx1151 (VMM crashes) — already in our build | done |
| Runtime load | --no-mmap -fa 1; --load-mode dio for fast loads (93 GB in ~33 s); MTP draft = biggest throughput lever (+74–106% measured) | A/B at benchmark time |
| Backend lanes | Vulkan for decode/interactive, ROCm for prefill/concurrency | benchmark decides |
Rejected with evidence (don't re-add): amd_iommu=off
(+5–12% prefill but breaks the NPU), xnack=1, vm_update_mode,
gartsize=131072, GGML_CUDA_ENABLE_UNIFIED_MEMORY (CUDA-only knob),
TDP/TDC/EDC chasing (box already sustains 120 W; decode is bandwidth-bound).
Contested (bench before believing): rocWMMA flash-attn flag,
ttm.page_pool_size, vm.compaction_proactiveness=20.
Full report: research/amd-ai-playbook-strix-halo.md.
Layout on disk
/srv/llm/ (owned by wg — survives dan-account removal)
├── src/llama.cpp/ source + build-vulkan/ + build-hip/
├── venv/ Python 3.14 + huggingface_hub CLI (model pulls)
└── models/ weights, one directory per model (2TB NVMe)
└── Qwen3.8-27B-GGUF/ UD-Q4_K_XL + mmproj-F16 (vision) + MTP draft
The OS keeps its own 1TB NVMe; wiping/replacing models never touches it.
Carried over from ashborn-prime (proven config)
- llama-swap tier +
evict_cost+ startup-preload design — with 128G unified memory the VRAM-eviction pressure that shaped the prime config mostly vanishes; co-residency becomes the default. - DRY sampling block (agent-safe):
--dry-multiplier 0.8 --dry-base 1.75 --dry-allowed-length 2, repeat-penalty 1.0, no presence penalty,--jinja,-n 8192cap. - Expert offload + big-ubatch ingest:
-ot ".ffn_...=CPU"pattern,-ncmoe,-ub 2048— re-validated on gfx1151 during benchmarking (CPU here is 16C/32T, strong for expert compute). - CPU rerank service: bge-reranker-v2-m3 Q8_0,
-ngl 0 -t 4 --rerank --pooling rank— unit file copied pattern. - MTP draft via embedded nextn head (≤2 draft caveat, ~1.7 GB draft context).
Endpoints (planned)
| Endpoint | Purpose | Status |
|---|---|---|
:8080 /v1 | OpenAI-compatible chat/completions front door (llama-swap) | pending |
:8080 /v1/embeddings | Embedding model (separate small GGUF) | model TBD |
:18190 /v1/rerank | CPU reranker (bge-reranker-v2-m3 Q8_0) | pending |
| tailnet :22 | SSH admin (wg + admin key) | live |
Bound to the tailnet interface only — the LAN-facing firewalld zone stays closed beyond ssh/mdns.
Ownership & service model
/srv/llmtree owned bywg(owner's account) —danis a temporary setup account and will be removed at handback.- All services will be systemd user units under
wg(lingering enabled), same pattern as ashborn-prime — no root-owned services. - firewalld untouched except by the owner; no inbound exposure beyond ssh.