Fully Local Agent Stack for DGX Spark

This is the complete self-hosted stack we run on the NVIDIA DGX Spark to power local autonomous agents without relying on cloud APIs for inference, search or speech recognition.

Every service below runs on the Spark itself or is reachable from other machines on the LAN through LiteLLM.


Architecture overview

┌─────────────────────────────────────────────────────────────┐
│                      Client layer                            │
│  Hermes (CLI/TUI) · Opencode · Open WebUI · n8n             │
└──────────────────────┬──────────────────────────────────────┘
                       │
                       ▼
┌─────────────────────────────────────────────────────────────┐
│                     LiteLLM proxy                            │
│     OpenAI-compatible gateway · port 4000 · 0.0.0.0         │
│  Routes to vLLM, exposes models, handles thinking toggle     │
└──────────────────────┬──────────────────────────────────────┘
                       │
                       ▼
┌─────────────────────────────────────────────────────────────┐
│                      vLLM inference                          │
│    Qwen3.8-Flash-Next (default) · Qwen 3.8 27B · Qwen 3.6   │
│    Gemma 4 · Nemotron 3 · port 8001                         │
└─────────────────────────────────────────────────────────────┘

Auxiliary services:
┌──────────────────┐  ┌──────────────────┐  ┌──────────────────┐  ┌──────────────────┐
│  SearXNG         │  │  faster-whisper  │  │  fastCRW         │  │  Browser / file  │
│  local search    │  │  ASR server      │  │  web extract     │  │  tools           │
│  port 8080       │  │  port 8001       │  │  port 3000       │  │  (optional)      │
└──────────────────┘  └──────────────────┘  └──────────────────┘  └──────────────────┘

Services

1. vLLM inference server

Serves the active LLM with an OpenAI-compatible API. Multiple model stacks can be installed; only one runs at a time because they share the unified memory pool.

PropertyValue (Qwen3.8-Flash-Next default)Value (Qwen 3.8 27B fallback)Value (Qwen 3.6 35B-A3B legacy)
URLhttp://localhost:8001/v1http://localhost:8001/v1http://localhost:8001/v1
Served nameqwen3.8-flash-nextqwen3.8-27b-nvfp4qwen3.6-35b-a3b
CheckpointRadixArk/Qwen3.8-Flash-Next-NVFP4unsloth/Qwen3.8-27B-NVFP4nvidia/Qwen3.6-35B-A3B-NVFP4
Containerqwen38-flash-dgx (vLLM release/qwen38next + 7 parches GB10)vllm/vllm-openai:v0.27.1-aarch64 + DSpark k=14vllm/vllm-openai:nightly
Launch scriptscripts/run-qwen38-flash-next.shscripts/run-qwen38-27b-nvfp4-dspark.shscripts/run-qwen36-35b-a3b.sh
Systemd unitqwen38-flash-next-vllm.service (enabled by default)qwen38-nvfp4-dspark-vllm.service (disabled, fallback)qwen36-vllm.service (disabled, legacy)
Max context262,144 tokens (500K YaRN)262,144 tokens (1M YaRN)262,144 tokens
Tool calling--tool-call-parser qwen3_coder--tool-call-parser qwen3_xml--tool-call-parser qwen3_coder
Reasoning parserqwen3qwen3(omit: nvidia checkpoint does not emit <think> tags)
MultimodalText + image + videoText + image + videoText + image + video
Memory~98 GB~105 GB~22 GB (1-seq/262K config)
Cold start~14 min~5-8 min~5-8 min

Switch models by stopping one systemd unit and starting another (one model at a time — they share the unified pool).

# Stop current default (Qwen3.8-Flash-Next) and start the 27B fallback
systemctl --user stop qwen38-flash-next-vllm.service
systemctl --user start qwen38-nvfp4-dspark-vllm.service

2. LiteLLM proxy

Unified gateway that exposes vLLM (and optional cloud fallbacks) under one OpenAI-compatible endpoint. Required when multiple clients share the backend or when accessing the Spark from another machine.

PropertyValue
URLhttp://0.0.0.0:4000/v1
Config~/litellm/config.yaml
Servicelitellm-proxy.service
Models exposed (active in 2026-09)qwen3.8-flash-next-vllm (default), 6 Ollama models (qwen3.5-9b, phi4, llama3.1-8b, qwen3.5-35b, gemma4-31b, nemotron3-33b), ollama/* wildcard, minimax-m3, minimax-m2.7, nomic-embed-text
Legacy models (in config for reference, require stopping default)qwen3.8-27b-nvfp4-vllm, qwen3.6-35b-heretic-vllm — both point to the same :8001 after the default swap
AuthDisabled (disable_user_auth: true); any non-empty Authorization header works

Restart after editing the config:

systemctl --user restart litellm-proxy.service

3. Hermes agent

Primary agent framework. Connected to LiteLLM so it can switch models and toggle thinking.

PropertyValue
Config~/.hermes/config.yaml
Providerlitellm-local
Default modelqwen3.8-flash-next-vllm (since 2026-09-01)
Reasoning overridesqwen3.8-flash-next-vllm: low; qwen3.8-27b-nvfp4-vllm: low (fallback)
Context length237,000 (forced in config)
Servicehermes-gateway.service
Hermes plugin~/.hermes/plugins/model-providers/litellm-local/ (maps Hermes reasoning levels to Qwen enable_thinking + reasoning_effort; advertises none, low, medium, xhigh; quietly maps minimal, high, max, ultra to the nearest Qwen native level)

See Agents for the full config and tool-calling notes.


4. Opencode

Editor-native agent client (the one driving this repo’s CI). Configured to point at the same LiteLLM proxy.

PropertyValue
Config~/.config/opencode/opencode.json
Default modelspark-litellm/qwen3.8-flash-next-vllm (since 2026-09-01)
Fallbackspark-litellm/qwen3.8-27b-nvfp4-vllm (same container after stopping the default)

Self-hosted meta-search engine. Hermes uses it as web.search_backend, removing the need for Firecrawl/Tavily/cloud APIs.

PropertyValue
URLhttp://localhost:8080
JSON APIhttp://localhost:8080/search?q=<query>&format=json
Config~/searxng/settings.yml
Compose~/searxng/docker-compose.yml
Servicesearxng.service
Engines usedDuckDuckGo, Brave, Bing, Google (aggregated)

6. faster-whisper ASR server

Local speech-to-text for voice messages received via Telegram/WhatsApp/etc.

PropertyValue
URLhttp://localhost:8001/v1
ModelSystran/faster-whisper-large-v3
Frameworkfaster-whisper 1.2.1 + FastAPI
DeviceCPU (int8)
Config in Hermesstt.provider: openai, base_url: http://localhost:8001/v1
Servicelocal-asr-server.service

⚠️ The ASR server also binds port 8001 (different scheme from the vLLM inference server on the same port). If you start the vLLM container on the default port the ASR container must move to a different port (or stop) because Docker cannot bind two processes to the same host port.


7. fastCRW local web extractor

Firecrawl-compatible web scraper and crawler. It gives Hermes the ability to extract clean markdown from URLs without calling cloud APIs.

PropertyValue
URLhttp://localhost:3000
Imageghcr.io/us/crw:latest
APIFirecrawl-compatible (/v1/scrape, /v1/crawl, /v1/search)
RAM~15–50 MB idle; ~200 MB peak
JS renderingLightPanda fallback (no Chromium baseline)
Search backendExisting SearXNG container (http://searxng:8080)
Config in Hermesweb.extract_backend: firecrawl, FIRECRAWL_API_URL=http://localhost:3000
Composeweb-extractor/docker-compose.yml
Servicefastcrw.service

Start it:

cd web-extractor
./run.sh

Or with systemd:

systemctl --user enable --now fastcrw.service

Test scrape:

curl -X POST http://localhost:3000/v1/scrape \
  -H "Authorization: Bearer local" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com","formats":["markdown"]}'

Ports summary

ServicePortReachable from LAN
vLLM (inference)8001No (localhost only)
LiteLLM4000Yes
Hermes gatewayvariesVia Telegram/Discord/etc.
SearXNG8080No (localhost only)
ASR server8001No (localhost only) — collides with vLLM port
fastCRW3000No (localhost only)

Only LiteLLM is exposed to the LAN by design. The other services are consumed locally by Hermes or through LiteLLM.


Systemd services

Enable/start everything after boot:

# Inference — default Qwen3.8-Flash-Next + fallback lite (27B) + legacy 3.6 heretic
systemctl --user enable --now qwen38-flash-next-vllm.service
# fallback lite, disabled by default:
systemctl --user enable --now qwen38-nvfp4-dspark-vllm.service
# legacy, disabled by default:
systemctl --user enable --now qwen36-heretic-vllm.service

systemctl --user enable --now litellm-proxy.service
systemctl --user enable --now hermes-gateway.service
systemctl --user enable --now local-asr-server.service
systemctl --user enable --now searxng.service
systemctl --user enable --now fastcrw.service

Check status:

systemctl --user status qwen38-flash-next-vllm.service
systemctl --user status qwen38-nvfp4-dspark-vllm.service
systemctl --user status qwen36-heretic-vllm.service
systemctl --user status litellm-proxy.service
systemctl --user status hermes-gateway.service
systemctl --user status local-asr-server.service
systemctl --user status searxng.service
systemctl --user status fastcrw.service

Mutually exclusive: qwen38-flash-next-vllm, qwen38-nvfp4-dspark-vllm, qwen36-heretic-vllm and qwen36-vllm all bind port 8001 and consume the entire GPU unified pool. Stop one before starting another.


Typical workflow

  1. Start the LLM:
    # Default (Qwen3.8-Flash-Next NVFP4 hybrid):
    systemctl --user start qwen38-flash-next-vllm.service
    # Or fallback lite (Qwen 3.8 27B + DSpark k=14):
    systemctl --user stop qwen38-flash-next-vllm.service
    systemctl --user start qwen38-nvfp4-dspark-vllm.service
  2. Verify LiteLLM and SearXNG are running.
  3. Launch Hermes:
    hermes chat
  4. Ask Hermes to search the web or transcribe a voice message — everything stays local.

Cloud dependencies remaining

With this stack, the only external calls are:

Inference, search, speech-to-text and web extraction are fully local.