Local AI Stack: Ollama + Open WebUI + Hermes Agent
A fully self-hosted local AI setup running on a single workstation — local LLM inference with Ollama, a browser chat UI with Open WebUI, and an autonomous, tool-using agent with Hermes Agent reachable from anywhere over Telegram.
Hardware used
- Host: Alienware Aurora R9 (Intel i9-9900K)
- GPU: NVIDIA RTX 2080 SUPER (8 GB VRAM)
- OS: Ubuntu 24.04 LTS
- Models: qwen3:8b / qwen2.5:7b (Q4_K_M)
8 GB of VRAM is the binding constraint throughout. Notes on what fits and where it spills to system RAM are called out below.
Why I built this
Most AI assistants today are cloud services — your prompts, your data, and your context all leave your network. For a homelab built around self-hosting and keeping data on my own hardware, that was the one piece I'd quietly outsourced without thinking about it.
I wanted to find out whether a genuinely useful AI agent — one that can search the web, run commands, remember context across sessions, and reach me on my phone — could run entirely on hardware I already own, with nothing leaving the network. Not a toy demo, but something I'd actually use day to day.
This page is the answer, written as a build guide so anyone can reproduce it. It includes the parts that didn't work the first time — the context-window limit, the tool-calling bug that needed a proxy to fix, and the honest tradeoffs of running models on a single 8 GB GPU. The stack works, and everything here transfers to bigger hardware when the homelab grows.
Before you start
You should be able to follow this end to end on a fresh Ubuntu machine. Here's what you need in place first.
Prerequisites
- A Linux host — this guide uses Ubuntu 24.04 LTS. Other distros work with minor command changes.
- An NVIDIA GPU with working drivers (optional but strongly recommended).
Verify with
nvidia-smi— it should list your card. CPU-only inference works but is much slower. - Docker installed — used for Open WebUI. Install with
sudo apt install docker.ioand add yourself to the docker group:sudo usermod -aG docker $USER(log out/in afterward). curlandgit—sudo apt install curl git.- A Telegram account — for the Hermes mobile gateway (optional; you can use the CLI instead).
- ~20 GB free disk space — for models, containers, and caches.
Order matters
Install in the order below — Ollama first (everything depends on it), then Open WebUI and Hermes as independent front-ends. Verify each piece works before moving to the next; it's far easier to debug one layer at a time than to untangle a broken full stack.
Overview
| Component | Role | Access |
|---|---|---|
| Ollama | Local model server (OpenAI-compatible API) | http://localhost:11434 |
| Open WebUI | Browser chat front-end | http://localhost:3000 (or your mapped port) |
| Hermes Agent | Autonomous tool-using agent + messaging gateway | Telegram / CLI |
| LiteLLM | Proxy that fixes Ollama's streaming + tool-call bug | http://localhost:11435 |
The three core pieces are independent. Ollama is the foundation; Open WebUI and Hermes are two different front-ends that both talk to it.
1. Install Ollama
Ollama is the local inference server. It exposes an OpenAI-compatible API that everything else connects to.
Verify the service is running:
Pull a model
For an 8 GB card, a 7–8B model at Q4 quantization is the practical ceiling.
ollama pull qwen3:8b # ~5.2 GB, strong tool-calling
# or
ollama pull qwen2.5:7b # ~4.7 GB, lighter, more context headroom
Confirm it loads and responds:
Keep the model warm
For an always-on agent, set these so the model stays loaded between requests and inference is faster:
OLLAMA_FLASH_ATTENTION=1 # faster attention, lower memory
OLLAMA_KV_CACHE_TYPE=q8_0 # ~halves KV cache size, keeps more in VRAM
OLLAMA_KEEP_ALIVE=-1 # never unload the model
Set them via systemctl edit ollama under a [Service] block as
Environment="KEY=value", then systemctl restart ollama.
2. Install Open WebUI
Open WebUI is a polished browser chat interface. The cleanest install is the official Docker image.
docker run -d \
--name open-webui \
-p 3000:8080 \
--add-host=host.docker.internal:host-gateway \
-v open-webui:/app/backend/data \
--restart always \
ghcr.io/open-webui/open-webui:main
Then open http://localhost:3000 in a browser, create the first account
(it becomes the admin), and Open WebUI auto-detects the local Ollama instance.
Container reaching Ollama
--add-host=host.docker.internal:host-gateway lets the container reach
Ollama running on the host. If Open WebUI can't see your models, check that
Ollama is bound to 0.0.0.0 (set OLLAMA_HOST=0.0.0.0 in the service env),
not just loopback.
3. Install Hermes Agent
Hermes is the autonomous agent layer — it adds tool use (web search, terminal, file ops, memory), persistent memory, cron jobs, and messaging-platform gateways on top of any OpenAI-compatible model.
The installer sets up uv, Python 3.11, and Node.js under ~/.hermes/, then
launches an interactive setup wizard.
PATH gap
The installer may not add ~/.local/bin to your PATH. If the hermes
command isn't found afterward, add this to ~/.bashrc:
Setup wizard choices
Walk through hermes setup and select:
- Provider: Ollama (
http://127.0.0.1:11434/v1) - Model: your pulled model (e.g.
qwen3:8b) - Terminal backend: Local
- Messaging: Telegram (paste a bot token from @BotFather, then lock the allowlist to your own Telegram user ID from @userinfobot)
- Search: SearXNG, pointed at your instance (e.g.
http://localhost:8888)
Run as a background service
systemctl --user restart hermes-gateway
loginctl enable-linger $USER # keep it running after logout / on boot
Gotchas & Fixes
These are the non-obvious issues encountered during setup. Documenting them because each one cost real time.
Context window floor (64K minimum)
Hermes refuses models reporting under 64K context. Most local models default to 32K, which triggers:
ValueError: Model qwen2.5:7b has a context window of 32,768 tokens,
which is below the minimum 64,000 required by Hermes Agent.
Two ways to resolve
Option A — override in config. Set context_length under the model:
block (top-level) in ~/.hermes/config.yaml. Note Hermes caches the
detected value in ~/.hermes/context_length_cache.yaml; delete that file
after editing config so it re-detects.
Option B — accept native context. A 7–8B model at a forced 64K spills past 8 GB VRAM into system RAM anyway. Running at the model's native 32K is often the better fit for the hardware.
Ollama streaming + tool-call bug (the big one)
The most important fix. Ollama hangs or mangles tool calls when streaming is on and many tool definitions are sent — which Hermes does on every request. The symptom is tools firing with broken arguments:
Tool memory returned error: {"error": "Unknown action 'None'"}
Tool web_search returned error: SearXNG returned HTTP 400 (empty q=)
This is not a model-size problem — it reproduces across 7B, 8B, and 14B
models identically. The fix is a LiteLLM
proxy that forces stream: false between Hermes and Ollama.
Create ~/litellm-config.yaml:
model_list:
- model_name: qwen3-local
litellm_params:
model: ollama_chat/qwen3:8b
api_base: http://127.0.0.1:11434
stream: false
Run the proxy:
Then point Hermes at the proxy instead of Ollama directly via hermes model →
Custom endpoint → http://127.0.0.1:11435/v1, model qwen3-local.
Result
With the proxy in place, tool calls return clean structured arguments —
memory writes to ~/.hermes/memories/USER.md, web search fires with a real
query, and the agent behaves reliably.
Disable thinking mode for cleaner tool calls
Qwen3's reasoning traces can confuse the tool-call parser. Bake a no-think variant with a Modelfile:
cat > qwen3-nothink.Modelfile << 'EOF'
FROM qwen3:8b
PARAMETER num_ctx 32768
SYSTEM /no_think
EOF
ollama create qwen3-nothink -f qwen3-nothink.Modelfile
Then reference qwen3-nothink in the LiteLLM config.
Persona via SOUL.md
For stable facts the agent should always know (without relying on the memory
tool firing), edit ~/.hermes/SOUL.md. It's injected into the system prompt
every session:
You are NikLabs Agent, a personal AI assistant running locally on the homelab.
Be direct, technical, and concise. The user's name is Nikhil...
Why small models (and what they can do)
The single biggest choice in this build is model size, and it's dictated by the 8 GB of VRAM on the RTX 2080 SUPER. Here's the reasoning, because it's the question anyone reproducing this will face first.
Why not a big model? A model has to fit in VRAM to run fast. At Q4 quantization, roughly:
| Model | Size (Q4) | Fits 8 GB? | Tool-calling | Notes |
|---|---|---|---|---|
| qwen2.5:7b | ~4.7 GB | Yes | Fair | Lightest, most context headroom |
| qwen3:8b | ~5.2 GB | Tight | Good | Best balance on 8 GB |
| qwen3:14b | ~9.3 GB | No (spills to RAM) | Excellent | Runs, but slow on 8 GB |
| qwen3:32b | ~20 GB | No | Excellent | Needs 24 GB VRAM |
A 14B model technically runs on 8 GB, but Ollama has to offload layers it can't fit into system RAM, and inference slows from seconds to 30–90 seconds per reply — too slow for an interactive assistant. A 32B model won't load meaningfully at all. So on 8 GB, a 7–8B model isn't a compromise, it's the right choice: it stays entirely in VRAM, responds quickly, and leaves room for context.
What a small (7–8B) model can do well:
- Hold a natural conversation and answer general questions
- Follow a defined persona (via
SOUL.md) - Fire a single tool reliably — web search, a terminal command, a file read
- Summarise a web result or a document
- Save and recall facts from memory (with the LiteLLM proxy in place)
Where small models struggle (and why bigger ones are "better" at agents):
- Chaining several tools in one task (search → summarise → save)
- Long autonomous multi-step plans without losing track
- Precise multi-argument tool calls under a heavy tool load
- Sustained reasoning across many turns
In short: a 7–8B model is a capable assistant you ask things, not a fully autonomous agent that executes long plans. That distinction is what more VRAM and a bigger model buys you.
If you outgrow 8 GB
Reliable agentic tool-calling at the 14B tier wants 12–16 GB VRAM minimum, and 24 GB for the 27–32B models that approach cloud quality. The whole software stack above transfers unchanged to bigger hardware — only the model underneath changes. Start small, prove the pipeline, upgrade the GPU later.
Example use case: a homelab assistant on your phone
Here's a concrete thing this setup does well, within a small model's comfort zone — a single-tool task you can trigger from Telegram anywhere.
Goal: ask the agent for a quick status check on the homelab and get the answer on your phone.
From Telegram:
The agent fires the terminal tool, runs the command on the host, reads the output, and replies in plain language — e.g. "Your root filesystem is 62% used, 38 GB free." One tool, one clean result, well within a 7–8B model's reliable range.
Other single-tool tasks that work reliably:
- "Search for today's top tech news and summarise the top three." → web search
- "Show me the contents of my nginx config." → file read
- "What's the status of the ollama service?" → terminal (
systemctl status) - "Remember that my QNAP NAS is at 192.168.1.x." → memory write
Build up gradually
Start with single-tool requests like these and confirm they're reliable before asking for anything that chains multiple tools. It's the realistic way to get dependable results from a local small model — and it's genuinely useful: a private, phone-accessible assistant that can check on and act on your own infrastructure, with nothing leaving the network.
Verify the build
If you've followed along, run these checks to confirm each layer works. This is how you know you've succeeded.
-
Ollama responds:
You should get a short reply. -
Open WebUI loads: visit
http://localhost:3000, log in, and confirm your model appears in the dropdown and answers a test message. -
LiteLLM proxy is up:
Returns JSON listingqwen3-local. -
Hermes gateway is running:
-
The agent uses tools — the real test. From Telegram (or the CLI), send:
Then check that the memory file was actually written: Ask "what is my name?" in a new message — it should answer correctly from memory. If it does, the full stack works end to end.
You're done
A working local AI agent: private, self-hosted, reachable from your phone, with tool use and persistent memory — and not a single token left your network.
Quick reference
# Ollama
ollama list # installed models
ollama ps # what's loaded in VRAM
# LiteLLM proxy (must be running for Hermes tool calls)
litellm --config ~/litellm-config.yaml --port 11435
# Hermes
hermes model # switch model/provider
hermes config edit # edit config.yaml
hermes logs errors | tail -30 # recent errors
hermes prompt-size # system-prompt + tool-schema breakdown
systemctl --user restart hermes-gateway # restart the gateway
# Memory check
ls -la ~/.hermes/memories/ # USER.md / MEMORY.md appear when memory writes