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 — the dan setup account was removed 2026-09-11.
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 | active |
| BIOS UMA Frame Buffer | 512 MB, explicitly not AUTO (AUTO fights the GTT strategy; capacity-only, no speed effect) | done — set to 512M 2026-09-09 |
| BIOS version | 3.06 (Jul 2026) available via fwupd — fixes a UMA-cap bug, slow-GRUB bug, real CVEs; downgrade supported | done — 03.06 flashed 2026-09-09 |
| 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/ production tree — build-vulkan/ + build-hip/ (serving lane)
│ ├── llama.cpp-fp4/ pinned-branch build (d3ca537 + qwen4exp patches) for Flash-Next FP4
│ └── llama.cpp-glm/ PR-branch build for GLM-5.3-Flash (agent experiment)
├── bench/ benchmark scripts · logs/ → all run outputs
├── venv/ Python 3.14 + huggingface_hub CLI (model pulls)
└── models/ weights, one directory per model (2TB NVMe):
Qwen3.8-27B (main+mmproj+MTP) · Flash-Next IQ1_S set · Flash-Next ROCmFP4 ·
GLM-5.3-Flash IQ2_XS · Llama-3.3-70B · Qwen3-Embedding · bge-reranker (4 quants)
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 (live)
| Endpoint | Purpose | Status |
|---|---|---|
:8081 /v1 | THE GATEWAY — endpoint of record for clients: OpenAI-compatible chat/completions with memory (mem0+Qdrant) injection, tracing (Phoenix), server-side persona; streams byte-for-byte | live 2026-09-11 |
:8080 /v1 | Raw llama-swap lane (admin/benchmark): 7 model ids — qwen38-27b, flashnext-iq1s, flashnext-fp4, glm53-flash, dsv4-flash, llama-3.3-70b, embed | live |
:18190 /v1/rerank | CPU reranker (BGE-Reranker-v2-M3 Q8_0) | live |
:9621 | LightRAG (grounding; separate service — gateway proxying is a roadmap follow-up). Bound to 127.0.0.1, exposed to the tailnet via tailscale serve --tcp 9621 | live |
:8188 | ComfyUI (mediagen). Bound to 127.0.0.1, exposed to the tailnet via tailscale serve --tcp 8188 | live |
:6006 | Phoenix tracing UI (OTLP ingest from the gateway) — localhost-only, not tailnet-exposed | live |
| tailnet :22 | SSH admin (wg — key auth; the dan account was removed 2026-09-11) | live |
| Firewall scope | 8080/8081/18190 reachable from tailnet CGNAT range (100.64.0.0/10) only; LAN rules for the owner's static-IP network are a handback item (Roadmap 5.3); public blocked. HTTPS via tailscale serve | live |
| Auth | API key required on all inference endpoints (llama-server --api-key per entry). Two independent keys are live (2026-09-12, gateway patched for multi-key): builder key in /srv/llm/secrets/api-key, owner key in /srv/llm/secrets/api-key-2 — both work on every endpoint; issued out-of-band | live |
Agent integration (Hermes profile template, model ids, client examples): Hermes Integration →
OpenWebUI is NOT installed on this box by design — the owner
sets it up on his own machine after handback (guide:
OpenWebUI →, with <BOX-IP> placeholders).
Ownership & service model
/srv/llmtree owned bywg(owner's account) — the temporary setup accountdanwas removed 2026-09-11; the box is single-user (wg, wheel).- All services run as systemd user units under
wg(lingering enabled), same pattern as ashborn-prime — no root-owned services. - firewalld: tailnet CGNAT rules currently allow 8080/8081/18190; planned lockdown keeps only AI ports (8081 gateway, 8080 admin, 18190 rerank, 8188 ComfyUI, 9621 LightRAG, 6006 Phoenix) + SSH. LAN rules for the owner's static-IP network are part of handback (see OpenWebUI guide).
- Access model after handback: the owner reaches every AI
endpoint over his LAN (static IP); the operator reaches it via Tailscale — which
STAYS on the box. Endpoint of record for clients is the gateway
:8081; raw:8080is the admin/benchmark lane.