Deployment guide

Deploy a local LLM on Strix Halo (Ryzen AI Max+ 395): llama.cpp from zero to first answer

The exact path we use on our own boxes: memory setup the BIOS won't tell you about, a pinned llama.cpp container on Vulkan, and the three checks that prove the server actually works — including the two failure modes that hide behind green statuses.

Platform:
Strix Halo (Ryzen AI Max+ 395)
Time to complete:
45 min
Updated:
9/1/2026

This is the deployment path we run on our own Ryzen AI Max+ 395 machines — the same boxes behind our published numbers. No theory, no options catalog: one working path, with the traps marked where we hit them. Performance expectations are deliberately absent — those live in the reports, measured properly.

You need: a Strix Halo box (any vendor), Linux with a recent kernel, Docker, and disk space for the model you pick.

Step 1 — fix the memory ceiling first

On Linux your models will live in GTT, not in the BIOS-dedicated VRAM slice, and the ceiling is a kernel parameter. Skipping this step is the first thing to rule out when a large model refuses to load on this hardware. Check the current limit:

cat /sys/module/ttm/parameters/pages_limit

Multiply by 4 KiB to get bytes. If it is a fraction of your RAM, raise it via the kernel command line (ttm.pages_limit=...) and reboot. The full explanation of why the BIOS slider is the wrong lever — with readings from our serving node — is in the memory allocation report. Leave the BIOS slice small.

Step 2 — get a model

Pick a GGUF artifact and note its exact revision. A 35B-class MoE at Q4_K_M is the sweet spot we serve daily on this hardware — it leaves room for context and a second model. Which quant to pick is a measured question; short version: start with Q4_K_M, not the biggest file that fits.

mkdir -p /var/lib/llm/models
# download your chosen .gguf into it, and record WHERE it came from —
# repository and revision. "main" moves; your notes should not.

Step 3 — run the server, pinned

The Vulkan build of llama.cpp’s server runs on this iGPU out of the box:

docker run -d --name llm-server \
  --device /dev/dri --device /dev/kfd \
  -v /var/lib/llm/models:/models \
  -p 8080:8080 \
  ghcr.io/ggml-org/llama.cpp:server-vulkan \
  -m /models/YOUR-MODEL.gguf \
  -ngl 999 -fa on -c 32768 \
  --host 0.0.0.0 --port 8080 --jinja

Two habits that will save you later. First, after it works, re-pull by digest (ghcr.io/ggml-org/llama.cpp@sha256:...) instead of the tag — tags move, and when a future update changes your behavior you will want to know exactly which build you were on. Second, if you change --port, know that the image’s built-in healthcheck still probes 8080: your container will show unhealthy while serving perfectly. We wrote up that exact trap after it bit us — either keep the internal port at 8080 or override the healthcheck.

Step 4 — ask the server what it actually runs

Do not trust your command line; trust the process:

curl -s localhost:8080/props | python3 -m json.tool | head -40

Read n_ctx, the model path, and the slot count. Defaults change between llama.cpp versions, and the flag you did not set is still part of your configuration — we learned this the expensive way.

Step 5 — the smoke test that actually proves something

An HTTP 200 proves nothing yet. Send a real request and check three things:

curl -s localhost:8080/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"messages":[{"role":"user","content":"Reply with one short sentence: what is inference?"}],
       "max_tokens":400,"temperature":0}' \
  | python3 -c "import json,sys; d=json.load(sys.stdin); m=d['choices'][0]['message']; \
    print('content:', repr((m.get('content') or '')[:120])); \
    print('finish:', d['choices'][0].get('finish_reason')); \
    print('tokens:', d.get('usage',{}).get('completion_tokens'))"

Check one: content is not empty. A reasoning model can burn the whole token budget thinking and return an empty answer under a perfect HTTP 200 — we measured how often, and if you see it, raise max_tokens or disable thinking. Check two: finish is stop, not length. Check three: count tokens from usage.completion_tokens if you ever measure anything — never from the number of streaming events.

Step 6 — decide about reasoning mode

If your model has a thinking mode, decide deliberately whether it stays on. For everyday chat and structured automation on this hardware we measured what it costs and what it bought — read that before shipping the default to your users.

Where to go next

← All guides