Back to blog
Ai NewsCoding

How to Set Up a Local Vector Database for Private Document Search

9 min read

Why local RAG exists

A local RAG setup keeps every byte of data on the machine it lives on — no document, chunk, or query ever leaves your own hardware. For businesses with genuinely sensitive documents (legal, medical, financial), this removes an entire category of vendor-trust question that a cloud-hosted vector database always carries. (sitepoint.com)

Note

"Local" here means the vector database and the embedding model both run on hardware you control — a laptop, an on-prem server, or a VPS you administer. It doesn't require avoiding all AI APIs; you can still call a hosted LLM for the final answer while keeping retrieval fully private.

The actual pipeline

Ingest documents, chunk them into manageable segments, generate vector embeddings for each chunk, store those vectors in a database, then at query time embed the user's question and retrieve the most semantically similar chunks to ground the AI's answer in your actual files. (sitepoint.com)

The two decisions that matter most before you touch a database are chunking strategy and embedding model choice — the vector database itself is a smaller lever than most setup guides imply.

Chunking: the part everyone underrates

Recursive character splitting at 512 tokens with 50–100 tokens of overlap is the benchmark-validated default for most RAG applications, scoring 69% accuracy in the largest real-document test of 2026. Factoid queries work well at 256–512 tokens, while analytical and multi-hop queries benefit from 512–1,024 token chunks. (premai.io)

Chroma's own research found recursive splitting delivers 85–90% recall at 400 tokens, while semantic chunking (splitting on meaning boundaries rather than fixed character counts) reaches 91–92% and is considered the gold standard for production in 2026. But smaller isn't automatically better: in the FloTorch 2026 benchmark, semantic chunking that produced fragments averaging just 43 tokens scored only 54% accuracy end-to-end — too little context per chunk hurts as much as too much. A January 2026 systematic analysis also identified a "context cliff" around 2,500 tokens where response quality drops sharply. (firecrawl.dev)

Warning

Overlap isn't free value. A January 2026 systematic analysis using SPLADE retrieval and Mistral-8B on the Natural Questions dataset found overlap provided no measurable retrieval benefit and only increased indexing cost. Start with 10–20% overlap as a default, but actually test whether removing it hurts your specific corpus before assuming it's worth the storage. (firecrawl.dev)

Embedding models: the local options

You need a model that turns text into vectors, and running it locally (via sentence-transformers or Ollama) keeps the entire pipeline — not just storage — off the network.

Model MTEB score Params Context License Notes
Qwen3-Embedding-8B 70.58 (multilingual) 8B 32,000 tokens Apache 2.0 Best open-source score, heaviest to run
BGE-M3 63.0 568M 8,192 tokens MIT Dense + sparse + multi-vector from one model
Nomic Embed Text v1.5 ~62 137M 8,192 tokens Apache 2.0 Fully open weights/code/training data; runs via Ollama
all-MiniLM-L6-v2 56.3 ~22M 256 tokens Apache 2.0 Default sentence-transformers starting point, <10ms on CPU

(premai.io)

BGE-M3 or Nomic Embed v2 are recommended for laptop users with 8GB RAM — both run comfortably and support hybrid (dense + keyword) retrieval. Nomic-Embed-Text-v1.5 is the most CPU-friendly option in this set at roughly 1,400 tokens/sec on CPU, and it's available directly on Ollama for fully offline inference with no external API dependency. all-MiniLM-L6-v2 is fast but its MTEB score of 56.3 makes it uncompetitive with the others for production retrieval quality — fine for a quick prototype, not for something you're shipping. (premai.io, local-ai-zone.github.io)

As of April 2026, three open embedding models match or beat OpenAI's text-embedding-3-large on retrieval accuracy in actual RAG benchmarks while costing $0 per million tokens — the gap between "free and local" and "paid and hosted" has effectively closed for embeddings specifically, even where it hasn't for generation. (local-ai-zone.github.io)

Real local-capable vector databases

Qdrant runs locally, in Docker, on Kubernetes, or as managed cloud — genuinely flexible deployment, and specifically good for filter-heavy RAG (retrieving within one customer's data, a permission boundary, a date range) because it applies filters before the vector search rather than after, which is both faster and more accurate. Chroma is open-source, stores embeddings with metadata, and supports local development directly — the same API that runs in a Python notebook scales to a production cluster. ChromaDB and FAISS both use minimal RAM, reportedly a few hundred MB even for thousands of documents, so this isn't an infrastructure-heavy undertaking to start. (braintrust.dev, sitepoint.com, datacamp.com)

Beyond those two, LanceDB, pgvector, and FAISS round out the realistic local options, each with a different tradeoff:

Database Local memory footprint Best for Cost (self-hosted) Notes
ChromaDB 4–8 GB handles millions of embeddings Prototyping through mid-scale production Under $30/mo Single VPS sufficient for 100K–few million chunks; in-process, no network latency
Qdrant ~8 GB recommended Complex filtering (legal, financial, multi-tenant) $30–$50/mo Rust-based, consistent latency
pgvector Adequate under 2–3M vectors Teams already running PostgreSQL $0 incremental if DB exists Needs tuning past a few million vectors
LanceDB Disk-efficient, handles larger-than-memory datasets Edge deployments, desktop apps, no-server workflows Under $30/mo for moderate corpora Superior batch ingestion/re-indexing
FAISS A few hundred MB Research, embedding into your own app $0 (library, not a DB) No persistence, replication, or multi-tenancy — it's a library, not a database

(4xxi.com, braintrust.dev)

The community recommendation in 2026 is consistent: Chroma for new RAG projects and prototypes, with a migration path to Qdrant (for filtering-heavy needs) or pgvector (if you already run Postgres and stay under roughly 10M vectors) once requirements grow past what a single lightweight instance handles. (4xxi.com)

How retrieval actually finds the right chunk: HNSW

Every one of these databases needs an indexing algorithm to avoid comparing a query vector against every stored vector one by one — that brute-force approach doesn't scale past a small corpus. The dominant answer in production systems is HNSW (Hierarchical Navigable Small World), an approximate nearest neighbor algorithm built on proximity graphs that connect each vector to a handful of others based on distance. (pyimagesearch.com)

HNSW organizes vectors into multiple layers. Search starts at the top layer, which has the fewest nodes, and the algorithm looks for a good entry point close to the query vector; it then descends through progressively denser layers, gathering a pool of candidate vectors that are close to the query at each level, until it reaches the bottom layer holding the full dataset. This layered approach means the algorithm skips most of the search space entirely rather than scanning everything, which is what makes it "approximate" — it trades a small amount of recall accuracy for a dramatic reduction in query time. For large-scale retrieval across thousands of documents, HNSW offers the best accuracy-latency balance of the widely available ANN approaches, which is why Chroma, Qdrant, Weaviate, and pgvector all use it (or a close variant) as their default index type. (pyimagesearch.com)

Practically, this matters for local setups because HNSW's memory footprint scales with the graph structure, not just the raw vectors — it's a meaningful chunk of why Qdrant recommends ~8GB RAM and Weaviate recommends 16GB even though the raw embedding data itself would fit in far less. If you're running on constrained hardware, this is the tuning knob (ef_construction, m, and similar parameters vary by database) worth understanding before you assume you need bigger hardware.

A minimal working example (Chroma)

This is the shape of a local pipeline end to end — ingest, embed, store, and query — using Chroma with a local sentence-transformers model. No API keys, no network calls beyond the initial model download.

import chromadb
from chromadb.utils import embedding_functions

# Local embedding model - runs entirely on your machine
embedding_fn = embedding_functions.SentenceTransformerEmbeddingFunction(
    model_name="BAAI/bge-m3"
)

# Persistent local client - data lives in ./chroma_db
client = chromadb.PersistentClient(path="./chroma_db")
collection = client.get_or_create_collection(
    name="internal_docs",
    embedding_function=embedding_fn,
)

# Ingest: chunk your documents upstream, then add them
collection.add(
    documents=[
        "Refund requests must be filed within 30 days of purchase.",
        "Employee expense reports over $500 require manager approval.",
    ],
    metadatas=[{"source": "policy.pdf", "page": 4}, {"source": "finance.pdf", "page": 2}],
    ids=["chunk-1", "chunk-2"],
)

# Query: embed the question, retrieve the closest chunks
results = collection.query(
    query_texts=["How long do I have to request a refund?"],
    n_results=2,
)
print(results["documents"])

Swap SentenceTransformerEmbeddingFunction for an Ollama-backed Nomic Embed call and PersistentClient for QdrantClient if you need filtering — the pipeline shape stays identical; only the storage and embedding backends change.

What you're actually trading off

A local setup avoids cloud data exposure but means you're responsible for your own uptime, backups, and scaling — a cloud vector database handles that for you at the cost of your data living on someone else's infrastructure. For a small internal knowledge base, local is genuinely low-effort; for something that needs to scale to many concurrent users, the operational tradeoff shifts. Pinecone, for comparison, is fully managed with zero operational overhead but scales in cost from $70–$300+/month standard up to $500–$1,500/month at 5M+ vectors — the price of not thinking about any of this yourself. (4xxi.com)

Tip

One useful reality check from the comparison data: the vector database choice accounts for maybe 5–10% of your RAG system's overall quality. Chunking strategy and retrieval pipeline design matter significantly more than which database you pick — so don't over-index on this decision at the expense of the chunking work above. (4xxi.com)

The practical starting point

For a first local RAG setup: Chroma or a local Qdrant instance, paired with BGE-M3 or Nomic Embed running locally via sentence-transformers or Ollama, and recursive chunking at 400–512 tokens as a starting point (test semantic chunking once you have a baseline). This gets you a fully private document search pipeline without needing to understand vector database internals deeply first — the minimal-RAM footprint on the Chroma/FAISS side means this runs fine on ordinary hardware, not just a dedicated server. Move to Qdrant or pgvector only once you have a concrete reason: heavy metadata filtering, an existing Postgres instance, or a corpus crossing a few million chunks.


Sources: SitePoint — Local RAG Without the Cloud: Private Document AI Setup, Braintrust — Best Vector Databases for RAG in 2026, 4xxi — Vector Database Comparison 2026: ChromaDB vs. Qdrant vs. pgvector vs. Pinecone vs. LanceDB, DataCamp — Best Vector Databases 2026, PremAI — Best Embedding Models for RAG (2026), Local AI Zone — Top Embedding AI Models 2026, Firecrawl — Best Chunking Strategies for RAG (2026), PremAI — RAG Chunking Strategies: The 2026 Benchmark Guide, PyImageSearch — Vector Search with FAISS: Approximate Nearest Neighbor (ANN) Explained

Get new posts as they publish

No spam — just the next post, straight to your inbox.

Keep reading

Discussion