====== llama.cpp: большие MoE-модели с NVMe + RAM на TrueNAS (Docker, Custom App) ======
===== Введение =====
Инструкция описывает, как на сервере **TrueNAS** запустить большую MoE-модель (**Qwen3.8-Flash-Next**, 80–190 GB), которая **не помещается целиком в RAM+VRAM**: веса лежат на NVMe-накопителе, горячая часть держится в оперативной памяти и в кэше на видеокарте. Движок — **llama.cpp** (''llama-server'', OpenAI-совместимый API), запуск — Docker-приложение через веб-интерфейс TrueNAS.
Главное отличие от [[:truenas:application:strata_qwen_server|Strata на TrueNAS]]: llama.cpp **работает на текущем драйвере NVIDIA 550** (проверено на сервере), обновлять TrueNAS не нужно, и модель не «закрепляет» RAM — память, занятая под веса, отдаётся другим приложениям по требованию.
**Статус инструкции.** На сервере проверено: образ llama.cpp запускается и видит GPU на драйвере 550.142, поддерживает архитектуру модели (''qwen4exp'') и нужные ключи. Сама модель на сервере ещё не запускалась, скорость не замерялась — такие места помечены **(проверить)**.
===== Почему это работает =====
Qwen3.8-Flash-Next — модель Mixture-of-Experts (данные из заголовка GGUF):
^ Параметр ^ Значение ^
| Архитектура | ''qwen4exp'' |
| Слоёв | 48 |
| Экспертов в слое | **512**, из них на каждый токен работают только **10** |
| Контекст | до 262 144 токенов |
На каждый токен читается лишь малая часть весов (~2% экспертов). Поэтому:
* **общие веса** (внимание, эмбеддинги) и KV-кэш — на GPU;
* **эксперты** — в файле на NVMe, отображённом в память (''mmap''): Linux сам держит в RAM часто нужные страницы и дочитывает остальное с диска;
* **самые горячие эксперты** — дополнительно в GPU-кэше (''--moe-cache-mib'').
Чем больше модель превышает свободную RAM, тем чаще чтения с NVMe и тем медленнее генерация — но запускается и работает.
===== Какие движки умеют NVMe + RAM и что подходит этому серверу =====
^ Движок ^ Как использует SSD ^ На этом сервере ^
| **llama.cpp** (''llama-server'') | ''mmap'' весов + эксперты MoE в RAM (''--cpu-moe'') + GPU-кэш экспертов (''--moe-cache-mib''); режим Direct I/O (''-lm dio'') | **работает сейчас** (драйвер 550, проверено) — **эта инструкция** |
| **Strata** | свой движок, часть экспертов в RAM, остальное с диска; на ПК — самый быстрый вариант | только после обновления до TrueNAS 25.10.7+ (нужен драйвер 580), закрепляет RAM — [[:truenas:application:strata_qwen_server|отдельная инструкция]] |
| **Ollama** (уже стоит на сервере) | внутри llama.cpp, ''mmap'' | проще, но меньше контроля над выгрузкой экспертов; поддержка ''qwen4exp'' **(проверить)** |
| **SGLang «SSD Expert Pack»** | эксперты на NVMe, Direct I/O, GPU-кэш; для гигантских моделей (DeepSeek-V4-Flash, Kimi-K3) | **(проверить)** — вероятно, нужен более новый драйвер; см. блог LMSYS, 2026-08 |
===== Сервер =====
^ Параметр ^ Значение ^
| Система | TrueNAS SCALE 25.04.2.6, драйвер NVIDIA **550.142** |
| CPU | Intel Core i5-14500 (6 P-ядер + 8 E-ядер, 20 потоков) |
| RAM | 62 GB |
| GPU | RTX 2000 Ada, 16 GB |
| NVMe | пул ''appData'' — 2 × Samsung 990 PRO 4 TB, ''/mnt/appData'' |
Проверка образа на сервере:
$ docker run --rm --gpus all ghcr.io/ggml-org/llama.cpp:server-cuda --version
version: 0.9.0-dev (build 12316, commit 5d4c8eeb2)
$ docker run --rm --gpus all ghcr.io/ggml-org/llama.cpp:server-cuda --list-devices
Available devices:
CUDA0: NVIDIA RTX 2000 Ada Generation (16075 MiB, 4178 MiB free)
''4178 MiB free'' — остальную видеопамять в этот момент держали другие приложения (см. «Шаг 1»). Образ (4.4 GB) уже скачан на сервер.
===== Шаг 1: Освободить видеокарту =====
На GPU сервера уже работают Ollama, Whisper, LibreTranslate, XTTS/VoxCPM — вместе ~12 из 16 GB. Для модели нужно хотя бы 8–10 GB свободных (общие веса + KV-кэш + кэш экспертов). На время работы остановите GPU-приложения: **Apps → Installed → (приложение) → Stop**. В первую очередь ''ollama'' — она держит загруженные модели в VRAM.
nvidia-smi --query-gpu=memory.used,memory.total --format=csv
Остальные приложения (Nextcloud, Jellyfin и т.д.) **можно не останавливать**: память под веса модели — это кэш страниц Linux, он вытесняется, когда памяти не хватает. Чем больше свободной RAM, тем быстрее генерация.
===== Шаг 2: Датасет для моделей на NVMe =====
**Datasets** → ''appData'' → **Add Dataset**:
* Name: ''llm-models''
* Dataset Preset: **Generic**
* **Advanced Options**:
* Record Size: **1M** (большие файлы читаются крупными блоками)
* Compression: **OFF** (GGUF почти не сжимается)
* ATime: **Off**
* Save.
Путь: ''/mnt/appData/llm-models''. Места — с запасом под модели (на пуле ~3.3 TB свободно).
**Кэш ZFS (ARC) и mmap.** Файл модели, читаемый через ''mmap'', на ZFS кэшируется дважды: в ARC и в кэше страниц Linux — RAM тратится впустую. Рекомендуется для этого датасета отключить кэширование данных в ARC (останутся только метаданные) — в **System → Shell** под root (''sudo -i''):
zfs set primarycache=metadata appData/llm-models
zfs get primarycache,recordsize,compression appData/llm-models
**(проверить)** влияние на скорость: сравните ''tokens/s'' до и после (см. «Шаг 7»). Вернуть: ''zfs set primarycache=all appData/llm-models''.
===== Шаг 3: Выбор модели =====
Все варианты — Qwen3.8-Flash-Next в GGUF. «Свободно RAM» на сервере: ~30–38 GB при работающих приложениях, ~55 GB при остановленных. Что не поместилось в RAM — читается с NVMe.
^ Вариант ^ Размер ^ Где взять ^ Ожидание на этом сервере **(проверить)** ^
| ''UD-Q2_K_XL'' | 80 GB | ''unsloth/Qwen3.8-Flash-Next-GGUF'' | быстрее всех, качество ниже |
| **RVN ''IQ3_S''** | 83 GB | уже скачан на ПК: ''F:\projects\inst\qwen-iq3s'' (8 файлов) | **рекомендуется для начала** — ничего не качать, только скопировать |
| ''UD-IQ3_XXS'' / ''UD-Q3_K_XL'' | 84 / 90 GB | Unsloth | близко к IQ3_S |
| ''UD-IQ4_XS'' | 94 GB | Unsloth | 4 бита, чуть больше чтений с диска |
| ''UD-Q4_K_XL'' | 111 GB | Unsloth | **лучше качество**; заметная часть экспертов — с NVMe, медленнее |
| ''UD-Q5_K_XL'' … ''Q8_0'' | 158–188 GB | Unsloth | запустится, но в основном с диска — медленно; имеет смысл после добавления RAM |
**Примечание про RVN IQ3_S.** Этот же набор файлов не принял движок Strata (ему нужны служебные тензоры в BF16), а llama.cpp такие типы тензоров (IQ3_S, IQ4_NL) читает. Архитектура в файле — ''qwen4exp'', её поддерживает текущий llama.cpp. **(проверить запуск)**
===== Шаг 4: Загрузить модель на сервер =====
==== Вариант А: скопировать RVN IQ3_S с ПК ====
- **Shares → Windows (SMB) Shares → Add**: Path ''/mnt/appData/llm-models'', Name ''llm-models'', Save (TrueNAS предложит включить службу SMB — согласиться). Доступ — вашему пользователю.
- На ПК (PowerShell):
New-Item -ItemType Directory -Force \\TRUENAS\llm-models\qwen-rvn-iq3_s | Out-Null
robocopy F:\projects\inst\qwen-iq3s \\TRUENAS\llm-models\qwen-rvn-iq3_s *.gguf /J /NP
83 GB по гигабитной сети — ~15 минут.
==== Вариант Б: скачать с Hugging Face прямо на сервер ====
В **System → Shell** (или SSH) — одноразовый контейнер, который скачивает выбранный квант и удаляется:
docker run --rm -v /mnt/appData/llm-models:/models python:3.12-slim sh -c \
"pip install -q -U huggingface_hub hf_xet && \
hf download unsloth/Qwen3.8-Flash-Next-GGUF --include '*UD-Q4_K_XL*' --local-dir /models/unsloth-ud-q4_k_xl"
ls -la /mnt/appData/llm-models/unsloth-ud-q4_k_xl/*
Для другого кванта поменяйте ''UD-Q4_K_XL'' в обоих местах. Загрузка возобновляется при повторном запуске. **(проверить точные имена папок в репозитории Unsloth)**
В YAML («Шаг 6») указывается **первый** файл набора (''...-00001-of-0000N.gguf''), остальные части llama.cpp подхватит сам.
===== Шаг 5: API-ключ =====
openssl rand -hex 24
Ниже — ''<КЛЮЧ>''. Без ключа сервер будет открыт всей сети.
===== Шаг 6: Custom App через веб-интерфейс =====
**Apps → Discover Apps → ⋮ → Install via YAML**:
* Name: ''llama-qwen''
* Custom Config:
services:
llama-qwen:
image: ghcr.io/ggml-org/llama.cpp:server-cuda-b12316
container_name: llama-qwen
restart: unless-stopped
ports:
- "30081:8080"
volumes:
- /mnt/appData/llm-models:/models:ro
command:
- -m
- /models/qwen-rvn-iq3_s/RVN-Qwen3.8-Flash-Next-IQ3_S-00001-of-00008.gguf
- --alias
- qwen3.8-flash-next
- --host
- 0.0.0.0
- --port
- "8080"
- --api-key
- "<КЛЮЧ>"
- -c
- "65536"
- -ngl
- "99"
- --cpu-moe
- --moe-cache-mib
- "6144"
- -fa
- "on"
- -ctk
- q8_0
- -ctv
- q8_0
- -t
- "6"
- --jinja
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
Что делает каждая строка:
^ Параметр ^ Смысл ^
| ''server-cuda-b12316'' | закреплённая версия llama.cpp (сборка 12316, CUDA 12 — работает на драйвере 550). Обновление — сменить номер сборки |
| ''30081:8080'' | снаружи порт **30081** (8080/8081 заняты, 30080 зарезервирован под Strata) |
| ''/models:ro'' | датасет с моделями, только чтение |
| ''-m'' | первый файл набора GGUF |
| ''--alias'' | имя модели в API (''/v1/models'') — его указывают клиенты |
| ''-c 65536'' | контекст 64K (больше — больше VRAM под KV-кэш) |
| ''-ngl 99'' | все слои на GPU… |
| ''--cpu-moe'' | …**кроме экспертов**: они остаются в RAM/mmap (а значит, на NVMe) |
| ''--moe-cache-mib 6144'' | 6 GB видеопамяти — кэш самых используемых экспертов. Если VRAM мало — уменьшить; если свободно больше — увеличить |
| ''-fa on'', ''-ctk/-ctv q8_0'' | Flash Attention и KV-кэш в 8 бит — меньше VRAM под контекст |
| ''-t 6'' | потоки CPU: по числу P-ядер i5-14500. **(проверить)** ''-t 12'' и ''-t 14'' |
| ''--jinja'' | шаблон чата из модели (нужен для вызова инструментов агентами) |
| ''deploy … nvidia'' | доступ к GPU (как у ваших ''whisper'' и ''libretranslate'') |
Лимит памяти и ''memlock'' **не нужны**: в режиме ''mmap'' (по умолчанию) память под веса отдаётся системе по требованию.
===== Шаг 7: Запуск и проверка =====
**Apps → Installed → llama-qwen → Logs**. Нормальная загрузка выглядит так (суть строк): модель найдена, ''CUDA0 model buffer'' — часть на GPU, ''CPU_Mapped model buffer'' — эксперты отображены из файла, затем ''server is listening on http://0.0.0.0:8080''. Загрузка — от десятков секунд до нескольких минут.
На сервере:
curl -s http://127.0.0.1:30081/health
curl -s http://127.0.0.1:30081/v1/models -H "Authorization: Bearer <КЛЮЧ>"
С ПК (PowerShell), с замером скорости — в ответе поле ''timings.predicted_per_second'' (токенов/с генерации):
'{"model":"qwen3.8-flash-next","messages":[{"role":"user","content":"Объясни кратко, чем list отличается от tuple в Python"}],"max_tokens":300}' | Out-File -Encoding utf8 req.json
curl.exe -s http://192.168.1.135:30081/v1/chat/completions -H "Authorization: Bearer <КЛЮЧ>" -H "Content-Type: application/json" --data-binary "@req.json"
**Первые запросы будут медленными** — эксперты ещё не в RAM и читаются с NVMe. После нескольких запросов «горячие» эксперты оседают в кэше, скорость растёт. Замеряйте 3–5 запросов подряд.
===== Шаг 8: Подстройка скорости =====
Меняйте по одному параметру (**Apps → llama-qwen → Edit**) и сравнивайте ''predicted_per_second'':
* **''--moe-cache-mib''** — больше кэша экспертов на GPU, если VRAM свободна (''nvidia-smi''). Обычно самый заметный эффект.
* **''--n-cpu-moe N''** вместо ''--cpu-moe'' — эксперты первых N слоёв на CPU, остальных — **целиком на GPU**. Например ''--n-cpu-moe 44'' — эксперты 4 последних слоёв в VRAM. Подбирать, пока хватает VRAM.
* **''-t''** — 6 / 12 / 14 потоков; E-ядра иногда замедляют.
* **''-c''** — меньше контекст → больше VRAM под эксперты.
* **Свободная RAM** — остановка тяжёлых приложений даёт больше места под кэш страниц → меньше чтений с NVMe.
* **''-lm dio''** (Direct I/O) — читать веса в обход кэша страниц. Обычно медленнее, имеет смысл только для экспериментов.
Активность диска: **Reports → Disk** (или ''zpool iostat -v appData 2'' под root) — при «прогретом» кэше чтений с NVMe во время генерации должно быть мало.
===== Шаг 9: Подключение клиентов =====
^ Параметр ^ Значение ^
| Тип API | OpenAI Compatible |
| Base URL | ''http://192.168.1.135:30081/v1'' |
| Chat Completions | ''http://192.168.1.135:30081/v1/chat/completions'' |
| API Key | ''<КЛЮЧ>'' |
| Модель | ''qwen3.8-flash-next'' (значение ''--alias'') |
==== Goose Desktop ====
Settings → Models → **Add Custom Provider**: Provider Type **OpenAI Compatible**, Display Name ''llama.cpp Server'', API URL ''http://192.168.1.135:30081/v1/chat/completions'', API Key ''<КЛЮЧ>'', Available Models ''qwen3.8-flash-next'', Streaming — вкл. Или файлом ''%APPDATA%\Block\goose\config\custom_providers\custom_llama_server.json'':
{
"name": "custom_llama_server",
"engine": "openai",
"display_name": "llama.cpp Server",
"description": "llama.cpp on TrueNAS 192.168.1.135:30081",
"api_key_env": "CUSTOM_LLAMA_SERVER_API_KEY",
"base_url": "http://192.168.1.135:30081/v1/chat/completions",
"models": [
{ "name": "qwen3.8-flash-next", "context_limit": 65536 }
],
"headers": null,
"timeout_seconds": 600,
"supports_streaming": true,
"requires_auth": true
}
Ключ — в Settings → Models у провайдера. Русский язык и MCP — как в [[:truenas:application:strata_qwen_goose|инструкции для ПК]].
==== Open WebUI (уже стоит на сервере) ====
Admin Panel → Settings → Connections → OpenAI API → URL ''http://192.168.1.135:30081/v1'', Key ''<КЛЮЧ>''.
Остальные клиенты (OpenCode, Claude Code, Python) — как на странице [[:truenas:application:strata_qwen_server|Strata на TrueNAS]], с адресом ''http://192.168.1.135:30081/v1'' и моделью ''qwen3.8-flash-next''.
===== Несколько моделей / смена модели =====
* **Сменить модель** — **Edit** → поменять путь в ''-m'' (и при желании ''--alias'') → Save.
* **Не запускайте одновременно** ''llama-qwen'' и ''strata'' (после обновления TrueNAS) — они делят одну видеокарту.
* Удалить ненужную модель — удалить её папку в ''/mnt/appData/llm-models''.
===== Безопасность =====
* Порт 30081 открыт всей домашней сети — ключ обязателен. Не пробрасывайте порт на роутере в интернет; доступ извне — через VPN или ''nginx-proxy-manager'' с HTTPS.
===== Проблемы и решения =====
==== unknown model architecture: 'qwen4exp' ====
Слишком старая сборка llama.cpp. Используйте свежий тег ''server-cuda-bNNNNN'' (поддержка добавлена в 2026-08, PR #27742).
==== Нет GPU в логах / no CUDA devices ====
В YAML нет блока ''deploy … nvidia'' или видеокарту не видит Docker: на сервере ''docker run --rm --gpus all ghcr.io/ggml-org/llama.cpp:server-cuda-b12316 --list-devices''.
==== CUDA out of memory при загрузке ====
Видеокарту держат другие приложения («Шаг 1») — либо уменьшите ''--moe-cache-mib'' и ''-c''.
==== Очень медленно (меньше 1–2 токенов/с) ====
* Кэш ещё не прогрет — сделайте несколько запросов.
* Мало свободной RAM — остановите тяжёлые приложения; проверьте ''free -g'' (столбец ''available'').
* Модель слишком большая для RAM этого сервера — возьмите квант меньше.
* Попробуйте ''primarycache=metadata'' («Шаг 2»).
==== Модель не найдена ====
Путь в ''-m'' — **внутри контейнера** (''/models/...''), а не ''/mnt/appData/...''. Проверка: ''ls /mnt/appData/llm-models/<папка>''.
===== Шпаргалка =====
docker logs -f llama-qwen
nvidia-smi
free -g
curl -s http://192.168.1.135:30081/v1/models -H "Authorization: Bearer <КЛЮЧ>"
^ Что ^ Значение ^
| Адрес API | ''http://192.168.1.135:30081/v1'' |
| Модель (API) | ''qwen3.8-flash-next'' |
| Модели на диске | ''/mnt/appData/llm-models'' (NVMe) |
| Образ | ''ghcr.io/ggml-org/llama.cpp:server-cuda-b12316'' |