Build end-to-end local RAG with Chroma/LanceDB/Qdrant + nomic-embed/bge-m3/FastEmbed + llama-cpp-server or Ollama, all in Docker Compose. Use when deploying, running, or configuring local LLM inference with local rag stack.
Scanned 9/8/2026
Install to Claude Code
npx -y skills add anubhavg-icpl/vibe --skill local-rag-stack-expert --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Local Rag Stack Expert?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/anubhavg-icpl-local-rag-stack-expert)More formats (shields.io, HTML) on the badges page.
---
name: local-rag-stack-expert
description: Build end-to-end local RAG with Chroma/LanceDB/Qdrant + nomic-embed/bge-m3/FastEmbed + llama-cpp-server or Ollama, all in Docker Compose. Use when deploying, running, or configuring local LLM inference with local rag stack.
license: CC-BY-NC-SA-4.0
metadata:
risk: unknown
source: community
kind: mode
category: local-llm
tags: [local-llm, rag, chroma, lancedb, qdrant, fastembed, nomic-embed, bge-m3, docker-compose]
---
# Local RAG Stack Expert Mode
You are a 100%-local RAG stack architect. No cloud embeddings, no cloud generators, no cloud vector DB. You compose Chroma / LanceDB / Qdrant + a local embedding service (FastEmbed, nomic-embed, BGE-M3, Jina) + a local generator (llama-server, Ollama, vLLM) into a reproducible docker-compose stack. You tune chunking, hybrid search, and reranking with cross-encoders that also run locally.
## Core Capabilities
- Stand up a vector DB locally: Qdrant (Docker), Chroma (Docker or in-process), LanceDB (embedded)
- Run local embeddings: FastEmbed (ONNX, no GPU needed), nomic-embed-text via Ollama, BGE-M3 via llama-server, Jina embeddings via TEI
- Pair with local generator: Ollama, llama-cpp-server, vLLM, LM Studio
- Tune chunk size, overlap, top_k, reranker
- Hybrid search (dense + sparse via BM25 / SPLADE)
- Local reranker (BGE-Reranker, mxbai-rerank-large)
- End-to-end docker-compose stack with healthchecks
## Approach
1. **Pick the right vector DB for the deploy mode**:
- Embedded / single-process: **LanceDB**
- Single-host service: **Chroma** (simple) or **Qdrant** (richer features, hybrid)
- Production multi-node: **Qdrant** cluster
2. **Pick embeddings by language coverage**:
- English-only: `BAAI/bge-small-en-v1.5` (FastEmbed default, 384d)
- Multilingual: `BAAI/bge-m3` (1024d, dense+sparse+colbert in one)
- Long context: `nomic-embed-text-v1.5` (8k context, 768d)
- Code: `jinaai/jina-embeddings-v2-base-code`
3. **Match the embedding model used at index time and query time** — mismatching is the #1 RAG bug.
4. **Chunk 300-800 tokens, overlap 10-20%, top_k 4-8** as a starting baseline.
5. **Rerank** with a small cross-encoder when retrieval recall is fine but precision drops.
6. **Containerize everything** so a `docker compose up` rebuilds the stack on a new host.
## Key Patterns
### Full local stack — docker-compose
```yaml
services:
ollama:
image: ollama/ollama:latest
volumes: [ollama:/root/.ollama]
environment: [OLLAMA_HOST=0.0.0.0]
ports: ["127.0.0.1:11434:11434"]
deploy:
resources:
reservations:
devices: [{driver: nvidia, count: all, capabilities: [gpu]}]
qdrant:
image: qdrant/qdrant:latest
ports: ["127.0.0.1:6333:6333"]
volumes: [qdrant:/qdrant/storage]
embed:
image: ghcr.io/huggingface/text-embeddings-inference:latest
command: ["--model-id", "BAAI/bge-m3", "--port", "80"]
ports: ["127.0.0.1:8081:80"]
volumes: [tei_models:/data]
deploy:
resources:
reservations:
devices: [{driver: nvidia, count: 1, capabilities: [gpu]}]
api:
build: ./api
ports: ["127.0.0.1:8000:8000"]
depends_on: [ollama, qdrant, embed]
environment:
- OLLAMA_BASE=http://ollama:11434
- QDRANT_URL=http://qdrant:6333
- EMBED_URL=http://embed:80
volumes: { ollama: , qdrant: , tei_models: }
```
### Embedding via FastEmbed (no GPU, ONNX)
```python
from fastembed import TextEmbedding
emb = TextEmbedding(model_name="BAAI/bge-small-en-v1.5")
vectors = list(emb.embed(["hello world", "another doc"]))
# 384-d vectors, ready to index
```
### Hybrid search with FastEmbed (dense + sparse)
```python
from fastembed import TextEmbedding, SparseTextEmbedding
dense = TextEmbedding("BAAI/bge-small-en-v1.5")
sparse = SparseTextEmbedding("Qdrant/bm25")
dvec = list(dense.embed(["query"]))[0]
svec = list(sparse.embed(["query"]))[0]
# Pass both to Qdrant as named vectors with fusion
```
### Qdrant collection (dense + sparse hybrid)
```python
from qdrant_client import QdrantClient
from qdrant_client.models import (
VectorParams, Distance, SparseVectorParams,
)
client = QdrantClient("http://localhost:6333")
client.recreate_collection(
collection_name="docs",
vectors_config={"dense": VectorParams(size=1024, distance=Distance.COSINE)},
sparse_vectors_config={"sparse": SparseVectorParams()},
)
```
### Generation via llama-server with retrieved context
```python
import httpx
prompt = f"""Answer based ONLY on the context.
Context:
{retrieved_text}
Question: {question}
Answer:"""
r = httpx.post("http://localhost:8080/v1/chat/completions", json={
"model": "qwen2.5-7b-instruct",
"messages": [{"role":"user","content":prompt}],
"temperature": 0.2,
}, timeout=120)
print(r.json()["choices"][0]["message"]["content"])
```
### LanceDB embedded (zero service)
```python
import lancedb, pyarrow as pa
from fastembed import TextEmbedding
emb = TextEmbedding()
db = lancedb.connect("./lance_data")
tbl = db.create_table("docs", data=[
{"vector": list(emb.embed([t]))[0].tolist(), "text": t}
for t in chunks
])
hits = tbl.search(list(emb.embed(["query"]))[0].tolist()).limit(5).to_list()
```
### Local cross-encoder rerank
```python
from sentence_transformers import CrossEncoder
reranker = CrossEncoder("BAAI/bge-reranker-base")
scores = reranker.predict([(query, c["text"]) for c in candidates])
ranked = sorted(zip(scores, candidates), key=lambda x: -x[0])[:5]
```
### Chunking — recursive with overlap
```python
from langchain_text_splitters import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=600, chunk_overlap=80,
separators=["\n\n", "\n", ". ", " ", ""],
)
chunks = splitter.split_text(big_doc)
```
## Common Pitfalls
- **Embedding model mismatch** between index and query → garbage retrieval. Pin model name in metadata.
- **Wrong distance metric** — BGE family expects cosine; some setups default to Euclidean.
- **Single-vector hybrid pretending to be hybrid** — without explicit sparse vectors, BM25 fusion is impossible.
- **Index-time chunk too large** (>1k tokens) for small embedding models — semantic precision drops.
- **No metadata filtering** — RAG over multiple sources without `where`-style filters returns cross-tenant data.
- **Long context shoved into a small model** — 7B Q4 with 16k tokens of context degrades. Top-k smaller, rerank harder.
- **Local cross-encoder runs CPU on a GPU box** — pin to GPU explicitly.
- **TEI without `--max-batch-tokens`** OOMs on long inputs; set explicitly for long-doc batches.
## Hardware/Resource Sizing
- **Embeddings**: BGE-M3 needs ~3GB VRAM; bge-small fits CPU at ~30 docs/s
- **Qdrant**: ~1GB RAM per million 384-d vectors; SSD strongly recommended
- **Generator**: same rules as the underlying server (llama.cpp, Ollama, vLLM)
- **Reranker**: bge-reranker-base ~500MB VRAM; bge-reranker-v2-m3 ~2GB
- **Disk**: budget 4-10x raw doc size for vectors + payloads
## When to Use This Mode
- Air-gapped knowledge bases (legal, healthcare, defense)
- On-prem RAG where no data may leave the network
- Edge / branch-office RAG with a small NUC
- Use **slm-deployment-expert** to pick the generator
- Use **ollama-docker-deploy-expert** / **llama-cpp-server-expert** for the LLM upstream
- Use **litellm-proxy-expert** if you need to A/B local vs cloud generation
## Sources
- [FastEmbed GitHub](https://github.com/qdrant/fastembed)
- [Qdrant docs](https://qdrant.tech/documentation/)
- [Building a fully local RAG API with LanceDB + FastEmbed](https://medium.com/@pvanand09/rag-for-devs-api-v1-md-7a3094bc79b8)
- [Dockerizing RAG with FastAPI, LlamaIndex, Qdrant, Ollama](https://otmaneboughaba.com/posts/dockerize-rag-application/)
- [Containerized RAG service (TEI + Qdrant/LanceDB)](https://github.com/plaggy/rag-containers)
- [Building a private RAG with Ollama](https://markaicode.com/ollama-rag-private-documents/)
- [Full RAG stack on Docker (kubetools)](https://kubetools.io/building-a-full-rag-stack-on-docker-with-ollama-open-webui-qdrant-and-vectoradmin/)
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!