RAG-System selbst hosten 2026 – Vektordatenbank + LLM

Was ist ein RAG-System – und warum 2026 selbst hosten?

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.

Architektur: Die fünf Komponenten eines RAG-Stacks

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.

Hardware-Anforderungen und echte Kosten 2026

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.

SetupGPU / VRAMModellKosten/Monat
EinstiegRTX 4000 SFF Ada, 20 GBQwen3-14B Q4 + BGE-M3ca. 184 € (Hetzner GEX44)
Produktiv kleinRTX 6000 Ada, 48 GBLlama 3.3 70B Q3 / Qwen3-32B Q5ca. 650–750 €
Produktiv groß2× L40S, 96 GB70B Q5, 32k Kontext, Batch 32ca. 1.200–1.500 €
CPU-onlyRyzen 9, 128 GB RAM8B Q4, 4–6 tok/sca. 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.

Vektordatenbanken im Vergleich: Qdrant, Weaviate, Milvus, pgvector

Die Wahl der Vektordatenbank bestimmt Betriebsaufwand und Skalierungsgrenze. Alle vier Kandidaten beherrschen HNSW und Filterung, unterscheiden sich aber deutlich in Reife und Ressourcenhunger.

SystemSpracheStärkeSchwäche
QdrantRustFilter + Vektor kombiniert, geringer RAM-Bedarf, saubere REST/gRPC-APIkein natives BM25, Sparse-Vektoren nötig
WeaviateGoHybrid Search out-of-the-box, Module für Rerankerhöherer RAM-Verbrauch, komplexere Konfiguration
MilvusGo/C++Milliarden Vektoren, GPU-Index (CAGRA)braucht etcd, MinIO, Pulsar – 3 Container Minimum
pgvectorCkein neuer Dienst, Transaktionen, SQL-Joinsab ~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.

Embedding-Modelle lokal betreiben

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.

LLM-Auswahl und Serving: Ollama vs. vLLM vs. llama.cpp

Drei Serving-Stacks dominieren 2026, und sie bedienen unterschiedliche Zwecke. Die Wahl falsch zu treffen kostet entweder Durchsatz oder Nerven.

StackDurchsatz (32B, Batch 32)VorteilNachteil
Ollama300–600 tok/sSetup in 5 Minuten, Modellverwaltung eingebautkein Continuous Batching über Requests
vLLM1.500–2.500 tok/sPagedAttention, Prefix-Caching, maximaler Durchsatzstrikte GPU-Anforderung, längere Konfiguration
llama.cpp150–400 tok/sCPU/GPU-Hybrid, GGUF-Quantisierung, minimale Abhängigkeitenwenig 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

Installation: Docker-Compose-Setup in 30 Minuten

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}}
  }'

Ingestion-Pipeline: Chunking, Metadaten, Inkremmente

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.

Retrieval-Qualität: Hybrid Search und Reranking

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.

Monitoring, Skalierung und Sicherheit

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.

Kostenvergleich: Selfhosting vs. OpenAI API

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.

VarianteMonatliche KostenAnmerkung
GPT-4o APIca. 480 €2,50 $/1M Input, 10 $/1M Output
GPT-4o-mini APIca. 32 €deutlich schwächer bei Fachfragen
Selfhost RTX 6000 Adaca. 700 €inkl. Strom, Wartung, unbegrenzte Tokens
Selfhost 2× L40Sca. 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.

FAQ

Wie viel VRAM brauche ich für ein RAG-System mit 70B-Modell?

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.

Reicht pgvector statt Qdrant für ein RAG-System?

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.

Welches Embedding-Modell ist für deutsche Dokumente am besten?

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.

Wie oft muss ich meinen Vektorindex neu aufbauen?

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.

Lohnt sich Selfhosting überhaupt gegenüber der OpenAI API?

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.