← Blog

AI agents

Ollama vs vLLM vs llama.cpp: on-prem inference voor AVG

Een Nederlandse gemeenteleverancier in Enschede draait 2.600 burgerintake-aanvragen per week op één RTX 6000 Ada. We vergeleken drie inference stacks op kosten, logs en on-call uren.

Jacob Molkenboer· Oprichter · A Brand New Company· 17 jun 2026· 9 min
Drie messing relais op ivoor papier, groen labeltje aan middelste, gevouwen formulier onder donkere steen.

Het is 07:42 op een zondag in Enschede. De RTX 6000 Ada in het half-rack bij een gemeenteleverancier met 23 medewerkers reageert niet meer. nvidia-smi hangt. Morgen om 08:30 moet de burgerintake-agent beginnen met het triageren van de maandagbatch: ongeveer 520 aanvragen tussen 08:30 en 12:00, daarna nog 280 in de middag. De on-call engineer is onderweg terug van haar moeder in Lonneker. De CISO leest de kernel-log op haar telefoon bij een koffie.

Dit is het moment waarop je keuze voor een inference stack écht uitmaakt. Niet de throughput-benchmark in een Mistral-Nemo-blogpost. Niet de tokens-per-seconde-curve bij batch size 64. Wat telt is: kun je rebooten, wie leest de logs, en wat zegt de Functionaris Gegevensbescherming als een burger volgende maand een Woo-verzoek indient over wat het model gezien heeft.

De afgelopen acht maanden hebben we precies dit soort workload gedraaid voor een Nederlandse leverancier die tussen gemeentes en burgers zit. 2.600 burgerintake-gesprekken per week. Eén GPU-bak. Een klein opsteam. Hieronder wat we leerden over de drie inference stacks waar iedereen in deze hoek uiteindelijk op uitkomt: Ollama, vLLM, en LM Studio gekoppeld aan llama.cpp.

De workload, in cijfers

De agent beantwoordt gestructureerde intake-vragen: wijzigingen in parkeervergunningen, opvolging van WMO-aanvragen, ophaalschema's voor afval, en de lange staart van 'ik kreeg deze brief, wat betekent dit'. Gemiddeld gesprek: acht beurten, ongeveer 1.400 input-tokens, ongeveer 600 output-tokens. Dat komt neer op zo'n 5,2 miljoen tokens per week als je beide kanten meetelt, of 270 miljoen per jaar voor vakanties en stormen.

Eén RTX 6000 Ada, 48 GB VRAM, in een gehuurd half-rack in een Nederlands datacenter met geauditeerde fysieke toegang. Mistral-Nemo-Instruct-2407 op 8-bit quant voor de front-line agent. Een kleinere fine-tune voor categorie-classificatie. Het geheel moet AVG-verdedigbaar en Woo-logbaar zijn: welke prompts gingen erin, welke outputs kwamen eruit, wie heeft ze gezien, wanneer zijn ze verwijderd. Niets verlaat het pand.

Ollama: degene die je in een weekend live krijgt

Ollama is de makkelijkste verkoop aan een dev-team van vier. Installeer, pull een model, wijs je code naar http://localhost:11434, ship. Met de Modelfile-abstractie kan een niet-Python-developer een system prompt en temperatuur instellen zonder CUDA aan te raken.

curl -fsSL https://ollama.com/install.sh | sh
ollama pull mistral-nemo:12b-instruct-2407-q8_0
sudo systemctl enable --now ollama

curl http://localhost:11434/api/chat -d '{
  "model": "mistral-nemo:12b-instruct-2407-q8_0",
  "messages": [{"role":"user","content":"Wat is een WMO-aanvraag?"}],
  "stream": false
}'

Waar Ollama zijn brood verdient is de operations-kant. De systemd-unit die het installeert is netjes. journalctl -u ollama laat zien wat er gebeurt. Als de RTX 6000 vastloopt, brengt een reboot 'm terug zonder een YAML-reload van 40 regels. Default logging is op request-niveau: timestamp, model, duur, token counts. Niet de prompts zelf; daarvoor wrap je de proxy.

De eerlijke kostenpost van Ollama bij onze workload is throughput. Single-stream KV cache, alleen batched over expliciet concurrent requests, geen PagedAttention. Bij 2.600 wekelijkse aanvragen zaten we rond de 38 tokens per seconde op Mistral-Nemo op int8, met piekconcurrency rond zes. Maandagochtend in de queue. Om 09:15 was 'ie leeg. Per 1k tokens, geamortiseerd over een GPU-lease van drie jaar, kwam dit uit op ongeveer €0,00021 input en €0,00021 output. Goedkoper dan elke commerciële API-tier, als je het onderhoud niet meerekent.

vLLM: degene die schaalt en je bijt

vLLM is de productiekeuze als je eerlijk bent over throughput. PagedAttention en continuous batching betekenen dat dezelfde RTX 6000 vier tot zeven keer meer tokens per seconde verwerkt bij concurrency boven vier. Voor onze workload betekende dat 180 tot 220 tokens per seconde op dezelfde Mistral-Nemo, met een queue die nooit voller werd dan twee requests diep. De vLLM OpenAI-compatible server docs lopen de configuratie-opties door.

python -m vllm.entrypoints.openai.api_server \
  --model mistralai/Mistral-Nemo-Instruct-2407 \
  --quantization fp8 \
  --max-model-len 8192 \
  --gpu-memory-utilization 0.90 \
  --max-num-seqs 16 \
  --disable-log-requests

Op papier is het kostenverhaal beter: ongeveer €0,00008 per 1k tokens bij onze concurrency, tegen dezelfde hardware-lease. Die besparing geef je terug op zondagochtenden. vLLM is een Python-proces met een wheel-keten die aan een specifieke CUDA-versie hangt. Als NVIDIA een driver-update pusht die de kernel op zaterdagavond automatisch toepast tijdens een reboot, start vLLM niet. De foutmelding is RuntimeError: CUDA error: no kernel image is available for execution on the device, en de fix is óf een wheel-rebuild óf een driver-downgrade. Allebei prima op een dinsdag. Geen van beide prima op 07:42 op zondag.

Waarschuwing

Als je voor vLLM kiest, pin dan de NVIDIA-driverversie expliciet. Hou de meta-package vast in apt, configureer unattended-upgrades om nvidia-* over te slaan, en zet de draaiende driverversie in je monitoring. Een stille driver-bump is in onze incident-log de meest voorkomende oorzaak van een vastgelopen zondagochtend.

Logging op vLLM is gestructureerd als je het aanzet. De --disable-log-requests flag bestaat met reden: standaard belanden request bodies op stdout. Dat is de verkeerde default voor elke deployment die BSN's, intake-formulieren of achternamen van burgers raakt. We sturen vLLM's structured access log naar een aparte aggregator, redacteren in de proxy-laag voordat het request bij vLLM aankomt, en houden de model server op INFO zonder request bodies.

LM Studio met llama.cpp: de desktop-stack die ons verraste

De derde stack begon als developer-speelgoed. LM Studio geeft je een desktop-UI om een quant te kiezen, te laden in de server-modus van llama.cpp, en daar een OpenAI-compatible client op aan te wijzen. Het is de stack die de homelab-threads op Hacker News blijven aanbevelen, en het is de stack die de CTO van onze klant al voor een weekendprototype had gebruikt voordat wij binnenkwamen.

Voor productie laat je de UI van LM Studio vallen en draai je llama-server uit llama.cpp direct. De argumenten zijn eerlijk:

./llama-server \
  --model models/Mistral-Nemo-Instruct-2407-Q8_0.gguf \
  --host 127.0.0.1 \
  --port 8080 \
  --n-gpu-layers 99 \
  --ctx-size 8192 \
  --parallel 8 \
  --cont-batching \
  --metrics

De llama.cpp-server is één static binary. Geen Python. Geen virtualenv. Geen wheel-pinning. Als CUDA vastloopt, rebuild je de binary met de nieuwe CUDA-versie in twaalf minuten vanaf een bekend commit. Je checkt een tag uit, draait cmake -B build -DGGML_CUDA=ON, kopieert de binary, restart de systemd-unit. Dezelfde vastgelopen zondag die vLLM twee uur kost, kost llama.cpp twintig minuten. We hebben het dit jaar twee keer gemeten.

Throughput zit tussen Ollama en vLLM in: ongeveer 90 tot 110 tokens per seconde op Mistral-Nemo op dezelfde quant, met continuous batching aan. Genoeg ruimte voor 2.600 wekelijkse aanvragen, met marge om het volume te laten verdubbelen voordat we opnieuw moeten nadenken. Per 1k tokens geamortiseerd: ongeveer €0,00015 over beide richtingen.

De logging is het schoonst van de drie. llama-server exposeert een /v1/chat/completions endpoint dat de OpenAI-vorm spiegelt, waardoor je AVG-verdedigbare logging in de proxy ervoor leeft, niet in de model server. Dat is de juiste plek. De model server hoort niet te weten wie de burger is.

Wat we uiteindelijk live zetten

Voor deze klant werd het llama.cpp met een dunne FastAPI-proxy ervoor. Drie redenen.

Eén. De on-call engineer kan op zondag from source rebuilden met een runbook dat op één A4 past. Dat kan vLLM niet beloven. De wheel-keten gaat ervan uit dat je een werkende development-omgeving hebt, de juiste CUDA-toolkit, en een uur. Geen van die dingen klopt op 07:42.

Twee. De logging-laag zit in onze code, niet in een third-party server. Als er een Woo-verzoek binnenkomt, weten we precies welke velden zijn vastgelegd, welke geredacteerd, en welke nooit zijn weggeschreven. De model server heeft geen debug-modus die een prompt body in journalctl kan lekken.

Drie. Throughput is voldoende. We hadden de factor vier van vLLM niet nodig. We hadden voorspelbare maandagen nodig.

Als je wekelijkse volume eerder 26.000 aanvragen is in plaats van 2.600, draait de rekensom om. Het kostenvoordeel per token van vLLM loopt op, en het operationele risico is te rechtvaardigen omdat je dan een dedicated platform engineer hebt en geen CTO die ook de SLA schrijft. Onder de 5.000 wekelijkse beurten wint llama.cpp. Boven de 20.000 wint vLLM. Daartussen kies je tussen zondaguren en throughput, en pak je degene die je team daadwerkelijk kan dragen.

AVG en Wet open overheid: het deel dat de benchmark-posts overslaan

De benchmark-posts vergelijken tokens per seconde. De auditor vergelijkt bewaartermijnen, verwerkingsdoelen, en de logregel die bewijst dat je geen BSN in een prompt hebt laten lekken.

Wat voor ons werkte: de guidance van de Autoriteit Persoonsgegevens over verwerkingslogging is uitgesproken over wat telt als een verdedigbare audit trail. Per request loggen we het volgende: timestamp, geanonimiseerd session id, intake-categorie, input token count, output token count, modelversie, versie van de redaction-pass. We loggen niet de prompt body of de model output in dezelfde store. De prompt body gaat, na PII-redactie, naar een aparte roterende store van zeven dagen met een gedocumenteerd doel. De model output gaat naar een store van dertig dagen die aan het dossier is gekoppeld.

Deze scheiding is makkelijker met llama.cpp omdat de model server dom is. Het logt geen prompt tenzij je het vraagt. Bij Ollama geldt hetzelfde, alleen is het logformaat minder gestructureerd. Bij vLLM moet je onthouden om --disable-log-requests te zetten, anders heb je net de WMO-vraag van een burger naar systemd-journald geschreven, en is de bewaarklok op die journalfile ook jouw probleem.

De Wet open overheid-kant wordt door gemeenteleveranciers vaak gemist. Een burger kan opvragen hoe zijn intake verwerkt is. 'Het model heeft besloten' is geen verdedigbaar antwoord. 'Hier is de getimestampte log van welke modelversie het request behandelde, welke redaction-pass eerst draaide, en welke categorie de agent toekende' wel. De keuze voor een inference stack zit upstream van die mogelijkheid, maar alleen als je de proxy bouwt die het vastlegt.

Het kleinste wat je vandaag kunt doen

Open een terminal op de GPU-bak die je hebt. Pin de NVIDIA-driverversie in je package manager, schrijf de huidige driverversie naar een bestand op /etc/abn/driver.lock, en voeg één regel toe aan je monitoring die alert als de draaiende driverversie afwijkt van het lockfile. Die ene check zou drie van de vijf vastgelopen zondagen hebben gevangen die we dit jaar bij gemeenteleveranciers hebben gezien.

Toen we de burgerintake-agent bouwden die op deze stack draait, was de gap die we steeds tegenkwamen die tussen wat de model server logt en wat de auditor wil zien. Uiteindelijk losten we het op door de hele AVG-verdedigbare laag in een FastAPI-proxy vóór de inference-laag te zetten, zodat de model server dom blijft en de audit trail leeft waar we erover kunnen redeneren.

Kern

Kies de inference stack die je on-call op zondag kan rebuilden vanuit een runbook van één A4. Throughput is een probleem voor dinsdag; vastgelopen drivers zijn er een voor zondag.

FAQ

Welke on-prem LLM-stack is het goedkoopst per 1k tokens?

vLLM bij hoge concurrency, llama.cpp in het midden, Ollama bij lage concurrency. Op geamortiseerde hardware zijn de verschillen klein; vLLM trekt pas weg boven ongeveer 20.000 wekelijkse beurten.

Is Ollama out-of-the-box AVG-compliant?

Geen enkele runtime is dat. Ollama logt standaard geen prompt bodies, wat helpt, maar compliance leeft in de proxy, het bewaarbeleid en de redaction-pass. Kies de stack, bouw daarna de audit-laag.

Kan llama.cpp de throughput van vLLM evenaren?

Niet bij hoge concurrency. Met continuous batching haalt het ongeveer de helft van vLLM's tokens per seconde op dezelfde hardware. Bij een paar duizend wekelijkse gesprekken maakt het verschil niet uit; boven de 20.000 wel.

Wat veroorzaakt een vastgelopen CUDA op zondag?

Bijna altijd een onbeheerde NVIDIA-driver-update die tijdens een onderhoudsreboot wordt toegepast. Pin de nvidia-* packages, hou de meta-package vast, en alert zodra de draaiende driverversie afwijkt van een lockfile.

ai agentsarchitectureoperationscase studytooling

Iets bouwen?

Start een project