
LLM lokal hosten 2026 – Komplettguide von der GPU bis zur API
LLM lokal hosten 2026: ✓ Hardware-Auswahl ✓ GPU-Vergleich (RTX 4090, RTX 5090, A6000) ✓ Ollama, vLLM & Docker ✓ Modell-Wahl ✓ OpenAI-kompatible API ✓ Schritt-für-Schritt-Anleitung.
Retrieval-Augmented Generation (RAG) kombiniert eine Vektordatenbank mit einem Large Language Model. Statt Wissen im Modellgewicht zu speichern, werden Dokumente in Chunks zerlegt, per Embedding-Modell in Vektoren umgewandelt und bei jeder Anfrage semantisch durchsucht. Die gefundenen Textstellen landen als Kontext im Prompt des LLM. Das Ergebnis: aktuelle, quellenbelegte Antworten ohne teures Fine-Tuning.
2026 sind die Argumente für Selfhosting deutlich konkreter geworden. Ein Qwen3-32B oder Llama 3.3 70B in 4-Bit-Quantisierung erreicht bei deutschsprachigen RAG-Aufgaben rund 90–95 % der Antwortqualität von GPT-4o – bei null Token-Kosten und voller Datenhoheit. Bei 5.000 internen Anfragen pro Tag mit je 4.000 Kontext-Tokens summiert sich die API-Rechnung schnell auf 900–1.400 € monatlich.
Der eigentliche Treiber ist aber Compliance. DSGVO, EU AI Act und interne Richtlinien verbieten es häufig, Kundendaten, Verträge oder Sourcecode an US-amerikanische Inferenz-Endpunkte zu senden. Ein lokal betriebener Stack aus Qdrant, BGE-M3 und vLLM löst dieses Problem strukturell – der Datenpfad verlässt nie das eigene Rechenzentrum.
Dieser Guide zeigt die komplette Architektur, konkrete Hardware-Kosten, ein lauffähiges Docker-Setup und die Retrieval-Optimierungen, die in der Praxis über Erfolg oder Frust entscheiden.
Ein produktives RAG-System besteht aus fünf klar trennbaren Schichten. Wer sie vermischt, debuggt später tagelang an falschen Stellen – schlechte Antworten kommen in 80 % der Fälle aus dem Retrieval, nicht aus dem LLM.
Dazu kommen zwei Querschnittsthemen: ein Orchestrierungs-Layer (LangChain, LlamaIndex oder ein eigener FastAPI-Service) und Observability mit Langfuse oder Phoenix, um Recall@k und Halluzinationsraten zu messen.
Für den Einstieg reicht ein einzelner Server mit einer GPU. Ab etwa 50 gleichzeitigen Nutzern trennt man Embedding und LLM auf zwei Maschinen, weil Embedding-Batches sonst die Token-Generierung blockieren.
Die Faustregel für VRAM lautet: Modellgröße in Mrd. Parametern × 0,6 GB bei 4-Bit-Quantisierung, plus KV-Cache. Der KV-Cache ist der unterschätzte Faktor – ein 32B-Modell mit GQA belegt bei 32.768 Token Kontext bereits 8–12 GB zusätzlich.
| Setup | GPU / VRAM | Modell | Kosten/Monat |
|---|---|---|---|
| Einstieg | RTX 4000 SFF Ada, 20 GB | Qwen3-14B Q4 + BGE-M3 | ca. 184 € (Hetzner GEX44) |
| Produktiv klein | RTX 6000 Ada, 48 GB | Llama 3.3 70B Q3 / Qwen3-32B Q5 | ca. 650–750 € |
| Produktiv groß | 2× L40S, 96 GB | 70B Q5, 32k Kontext, Batch 32 | ca. 1.200–1.500 € |
| CPU-only | Ryzen 9, 128 GB RAM | 8B Q4, 4–6 tok/s | ca. 60–90 € (AX42) |
Wer nur testen will, mietet stundenweise: RTX 4090 bei Vast.ai oder RunPod kostet 0,35–0,55 $/h, eine A100 80 GB liegt bei 1,20–1,80 $/h. Für einen Benchmark-Marathon über ein Wochenende ist das günstiger als jeder Monatsvertrag.
RAM und NVMe sind ebenfalls relevant. Rechnen Sie mit 1 GB RAM pro 1 Mio. Vektoren bei 1024 Dimensionen und int8-Quantisierung. Eine Million Chunks mit Payload belegen auf NVMe etwa 4–6 GB – bei 10 Mio. Chunks sollten Sie auf eine dedizierte NVMe mit ≥ 500 GB und ≥ 3.000 MB/s setzen.
Die Wahl der Vektordatenbank bestimmt Betriebsaufwand und Skalierungsgrenze. Alle vier Kandidaten beherrschen HNSW und Filterung, unterscheiden sich aber deutlich in Reife und Ressourcenhunger.
| System | Sprache | Stärke | Schwäche |
|---|---|---|---|
| Qdrant | Rust | Filter + Vektor kombiniert, geringer RAM-Bedarf, saubere REST/gRPC-API | kein natives BM25, Sparse-Vektoren nötig |
| Weaviate | Go | Hybrid Search out-of-the-box, Module für Reranker | höherer RAM-Verbrauch, komplexere Konfiguration |
| Milvus | Go/C++ | Milliarden Vektoren, GPU-Index (CAGRA) | braucht etcd, MinIO, Pulsar – 3 Container Minimum |
| pgvector | C | kein neuer Dienst, Transaktionen, SQL-Joins | ab ~5 Mio. Vektoren Indexaufbau zäh |
Für 90 % der Selfhosting-Szenarien ist Qdrant die pragmatische Wahl: ein Container, 512 MB RAM im Leerlauf, exzellente Payload-Filter. Wer bereits PostgreSQL betreibt und unter 2 Mio. Chunks bleibt, fährt mit pgvector 0.8 und HNSW-Index am günstigsten – ein Backup weniger.
Ein häufiger Fehler: zu viele Dimensionen. BGE-M3 liefert 1024 Dimensionen, unterstützt aber Matryoshka-Truncation auf 512 oder 256. Bei 256 Dimensionen sinkt der Recall nur um 2–4 %, während der Speicherbedarf und die Suchlatenz um 60–75 % fallen.
Embeddings sind der billigste Teil des Stacks und werden dennoch oft vernachlässigt. Ein gutes mehrsprachiges Modell ist Pflicht, sobald deutsche Fachbegriffe im Spiel sind – englische Modelle verlieren hier 15–25 % Recall.
Der Durchsatz entscheidet über die Ingestion-Dauer. Auf einer RTX 4090 verarbeitet BGE-M3 etwa 400–600 Chunks à 512 Token pro Sekunde. Eine Million Chunks sind damit in rund 30–40 Minuten eingebettet. Auf einer 8-Kern-CPU sinkt der Wert auf 20–40 Chunks/s – dieselbe Datenmenge braucht dann 7–14 Stunden.
# Embedding-Service als OpenAI-kompatibler Endpunkt
pip install vllm==0.8.5
vllm serve BAAI/bge-m3 \
--task embed \
--max-model-len 8192 \
--gpu-memory-utilization 0.30 \
--port 8001
Wichtig: Embedding-Modell und Query-Modell müssen identisch sein. Ein Wechsel des Embedding-Modells erfordert immer eine vollständige Neuindexierung – planen Sie das von Anfang an als Routineoperation ein und versionieren Sie den Collections-Namen, etwa dokumente_bgem3_v2.
Drei Serving-Stacks dominieren 2026, und sie bedienen unterschiedliche Zwecke. Die Wahl falsch zu treffen kostet entweder Durchsatz oder Nerven.
| Stack | Durchsatz (32B, Batch 32) | Vorteil | Nachteil |
|---|---|---|---|
| Ollama | 300–600 tok/s | Setup in 5 Minuten, Modellverwaltung eingebaut | kein Continuous Batching über Requests |
| vLLM | 1.500–2.500 tok/s | PagedAttention, Prefix-Caching, maximaler Durchsatz | strikte GPU-Anforderung, längere Konfiguration |
| llama.cpp | 150–400 tok/s | CPU/GPU-Hybrid, GGUF-Quantisierung, minimale Abhängigkeiten | wenig Multi-User-Features |
Für einen einzelnen Nutzer oder ein kleines Team ist Ollama völlig ausreichend. Sobald mehrere Personen parallel arbeiten, ist vLLM Pflicht – das Prefix-Caching zahlt sich bei RAG besonders aus, weil der Systemprompt und der abgerufene Kontext bei ähnlichen Fragen oft zu 60–80 % identisch sind.
Bei der Modellwahl gilt: Für deutschsprachiges RAG sind Qwen3-32B, Mistral Small 3.2 24B und Llama 3.3 70B die soliden Optionen. Ein 14B-Modell reicht, wenn der Kontext sauber gerankt ist – Retrieval-Qualität schlägt Modellgröße fast immer.
# vLLM mit AWQ-Quantisierung und Prefix-Caching
vllm serve Qwen/Qwen3-32B-AWQ \
--quantization awq \
--max-model-len 32768 \
--gpu-memory-utilization 0.90 \
--enable-prefix-caching \
--tensor-parallel-size 1 \
--port 8000
Das folgende Setup bringt Qdrant, Ollama und Open WebUI in einem Compose-File auf einen GPU-Server. Voraussetzung: Ubuntu 24.04, NVIDIA-Treiber ≥ 550, Docker 27 mit nvidia-container-toolkit.
# nvidia-container-toolkit installieren
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey \
| sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
sudo apt install -y nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
# docker-compose.yml
services:
qdrant:
image: qdrant/qdrant:v1.13.4
restart: unless-stopped
ports: ["6333:6333", "6334:6334"]
volumes: ["./qdrant_storage:/qdrant/storage"]
environment:
QDRANT__SERVICE__API_KEY: "${QDRANT_API_KEY}"
ollama:
image: ollama/ollama:0.6.8
restart: unless-stopped
ports: ["11434:11434"]
volumes: ["./ollama:/root/.ollama"]
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
openwebui:
image: ghcr.io/open-webui/open-webui:v0.6.10
restart: unless-stopped
ports: ["3000:8080"]
depends_on: [qdrant, ollama]
environment:
OLLAMA_BASE_URL: "http://ollama:11434"
VECTOR_DB: "qdrant"
QDRANT_URI: "http://qdrant:6333"
QDRANT_API_KEY: "${QDRANT_API_KEY}"
RAG_EMBEDDING_ENGINE: "ollama"
RAG_EMBEDDING_MODEL: "bge-m3"
RAG_TOP_K: "8"
CHUNK_SIZE: "1200"
CHUNK_OVERLAP: "200"
volumes: ["./openwebui:/app/backend/data"]
Starten Sie den Stack mit docker compose up -d und laden Sie das Modell: docker exec -it ollama ollama pull qwen3:14b. Anschließend legen Sie die Collection manuell an, um HNSW und Quantisierung zu kontrollieren – Open WebUI würde sonst Defaults verwenden.
curl -X PUT http://localhost:6333/collections/dokumente \
-H "api-key: $QDRANT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"vectors": {"size": 1024, "distance": "Cosine"},
"hnsw_config": {"m": 16, "ef_construct": 200},
"optimizers_config": {"default_segment_number": 4},
"quantization_config": {"scalar": {"type": "int8", "always_ram": true}}
}'
Chunking ist die wichtigste Einzelentscheidung im gesamten Stack. Zu kleine Chunks zerstören den Zusammenhang, zu große verwässern das Embedding. Bewährt haben sich 800–1.200 Token mit 15–20 % Overlap bei Fließtext und semantisches Chunking an Überschriften bei technischer Dokumentation.
Speichern Sie immer Payload-Metadaten mit: source, page, heading_path, updated_at, tenant. Ohne heading_path kann das LLM später keine sinnvolle Quellenangabe erzeugen, und ohne tenant ist Mandantentrennung unmöglich.
from qdrant_client import QdrantClient, models
import httpx, hashlib
qdrant = QdrantClient(url="http://localhost:6333", api_key=QDRANT_API_KEY)
def embed(texts: list[str]) -> list[list[float]]:
r = httpx.post(
"http://localhost:8001/v1/embeddings",
json={"model": "BAAI/bge-m3", "input": texts},
timeout=120,
)
return [d["embedding"] for d in r.json()["data"]]
def upsert_chunks(chunks: list[dict], batch: int = 64):
for i in range(0, len(chunks), batch):
part = chunks[i:i + batch]
vectors = embed([c["text"] for c in part])
points = [
models.PointStruct(
id=hashlib.md5(c["text"].encode()).hexdigest(),
vector=v,
payload={
"text": c["text"],
"source": c["source"],
"heading_path": c.get("heading_path", ""),
"updated_at": c["updated_at"],
"tenant": c.get("tenant", "default"),
},
)
for c, v in zip(part, vectors)
]
qdrant.upsert(collection_name="dokumente", points=points)
Für inkrementelle Updates nutzen Sie deterministische IDs (Hash über den Chunk-Text). Geänderte Dokumente erzeugen dann neue IDs, alte werden per Filter auf source gelöscht. Ein vollständiger Reindex von 500.000 Chunks dauert auf einer RTX 4090 etwa 20 Minuten – das ist als nächtlicher Cronjob völlig vertretbar.
Reines Dense Retrieval scheitert bei exakten Begriffen: Artikelnummern, Funktionsnamen, Akronyme. Die Lösung ist Hybrid Search – Dense-Vektoren plus Sparse-Vektoren (BM25-ähnlich), fusioniert per Reciprocal Rank Fusion.
Qdrant unterstützt Sparse-Vektoren nativ. BGE-M3 liefert beide Repräsentationen in einem Durchlauf, sodass Sie nur eine Collection mit zwei Vektorfeldern benötigen. Die Fusion erfolgt serverseitig über die Query-API mit prefetch und fusion=rrf.
curl -X POST http://localhost:6333/collections/dokumente/points/query \
-H "api-key: $QDRANT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"prefetch": [
{"query": {"nearest": [0.12, -0.44, "..."], "using": "dense"}, "limit": 50},
{"query": {"nearest": {"indices": [12, 884, 9021], "values": [0.8, 0.5, 0.3]}, "using": "sparse"}, "limit": 50}
],
"query": {"fusion": "rrf"},
"limit": 20,
"filter": {"must": [{"key": "tenant", "match": {"value": "einkauf"}}]}
}'
Danach folgt ein Cross-Encoder-Reranker. bge-reranker-v2-m3 benötigt auf einer RTX 4090 für 50 Kandidaten à 512 Token etwa 60–90 ms und hebt den Recall@5 typischerweise um 12–20 Prozentpunkte. Das ist die günstigste Qualitätssteigerung im gesamten Stack – vor jedem Modell-Upgrade zuerst hier optimieren.
Stellen Sie ef_search bewusst ein: Der Default von 128 ist für kleine Collections zu hoch. Bei 100.000 Vektoren reicht hnsw_ef=64 für 97 % Recall bei halbierter Latenz.
Messen Sie vier Metriken kontinuierlich: Retrieval-Recall@k gegen ein kuratiertes Testset aus 100–200 Fragen, Time-to-First-Token, Tokens/s und Halluzinationsrate via LLM-as-Judge. Ohne diese Zahlen optimieren Sie blind.
Für Tracing empfiehlt sich Langfuse (self-hosted, ein Docker-Compose) oder Arize Phoenix. Beide protokollieren Prompt, abgerufene Chunks und Antwort – genau die Daten, die Sie für Fehleranalysen brauchen.
Sicherheit ist bei RAG kein Nebenthema. Drei Maßnahmen sind Pflicht: API-Keys für Qdrant und vLLM (vLLM via --api-key), Netzwerktrennung – die Vektordatenbank darf nie öffentlich erreichbar sein – und Prompt-Injection-Filter auf den abgerufenen Chunks, da eingeschleuste Dokumente sonst Systemanweisungen überschreiben können.
Skalierung erfolgt in drei Stufen: Erst Reranker auf eine zweite GPU auslagern, dann Embedding-Service trennen, zuletzt Qdrant im Cluster-Modus mit drei Nodes und Replication Factor 2 betreiben. Ab etwa 200 gleichzeitigen Nutzern wird ein Load Balancer vor zwei vLLM-Instanzen mit geteiltem Prefix-Cache sinnvoll.
Die Rechnung kippt früher als viele denken. Angenommen werden 4.000 Anfragen pro Tag, je 1.500 Prompt-Tokens (davon 1.000 RAG-Kontext) und 400 Ausgabe-Tokens.
| Variante | Monatliche Kosten | Anmerkung |
|---|---|---|
| GPT-4o API | ca. 480 € | 2,50 $/1M Input, 10 $/1M Output |
| GPT-4o-mini API | ca. 32 € | deutlich schwächer bei Fachfragen |
| Selfhost RTX 6000 Ada | ca. 700 € | inkl. Strom, Wartung, unbegrenzte Tokens |
| Selfhost 2× L40S | ca. 1.350 € | trägt ~15.000 Anfragen/Tag |
Bei 4.000 Anfragen/Tag ist die API also noch günstiger – der Break-even gegenüber GPT-4o liegt bei etwa 6.000 Anfragen täglich. Der eigentliche Gewinn liegt nicht im Preis, sondern in Datenhoheit, Planbarkeit und der Möglichkeit, eigene Embedding- und Reranking-Modelle frei einzusetzen.
Ein oft übersehener Kostenblock: Der Betriebsaufwand. Rechnen Sie mit 0,2–0,5 Vollzeitäquivalenten für Updates, Reindexierung und Monitoring. Wer diese Personalkosten scheut, sollte einen Managed-RAG-Dienst prüfen, statt einen halb gewarteten Stack zu betreiben.
Ein Llama-3.3-70B in Q4_K_M benötigt rund 40 GB für die Gewichte. Dazu kommen KV-Cache und Aktivierungen: Bei 16.384 Token Kontext und Batch 8 sind das weitere 12–18 GB. Realistisch brauchen Sie also 64–80 GB VRAM, etwa zwei L40S oder eine H100. Für Einsteiger ist ein 14B- oder 32B-Modell mit gutem Retrieval die wirtschaftlichere Wahl.
Bis etwa 2 Mio. Vektoren mit 1024 Dimensionen ist pgvector 0.8 mit HNSW-Index problemlos nutzbar und spart einen zusätzlichen Dienst. Darüber hinaus steigen Indexaufbau-Zeiten und RAM-Bedarf überproportional. Sobald Sie Hybrid Search mit Sparse-Vektoren oder Mandantentrennung mit hoher Filterrate benötigen, ist Qdrant die bessere Wahl.
BGE-M3 ist 2026 der beste Kompromiss aus Recall, Kontextlänge (8.192 Token) und Ressourcenbedarf. multilingual-e5-large-instruct ist bei kurzen, instruktionsartigen Texten leicht überlegen. Qwen3-Embedding-4B erzielt die höchsten Recall-Werte bei langen Fachdokumenten, kostet aber 8 GB VRAM im FP16 und ist damit für Embedding-Services auf Shared-GPUs unpraktisch.
Nur bei einem Wechsel des Embedding-Modells oder der Chunking-Strategie ist ein vollständiger Reindex nötig. Für inhaltliche Änderungen genügt inkrementelles Upsert mit deterministischen IDs: geänderte Chunks überschreiben, gelöschte per Filter auf source entfernen. Planen Sie dennoch einen monatlichen Voll-Reindex als Qualitätssicherung ein – Fragmentierung durch häufige Updates kostet sonst 5–10 % Recall.
Rein monetär kippt die Rechnung erst bei etwa 6.000 Anfragen pro Tag gegenüber GPT-4o. Der eigentliche Grund für Selfhosting ist Datenhoheit: DSGVO-Konformität, keine Übermittlung von Kundendaten an Dritte und volle Kontrolle über Modellversionen. Wenn diese Anforderungen nicht bestehen, ist eine API-Lösung bei kleinen Volumina günstiger und wartungsärmer.