CloudCodeTree LogoCloudCodeTree
AI NewsTutorialsAbout
CloudCodeTree Logo
CloudCodeTree
  • AI News
  • Tutorials
  • About
← Back to AI News
No Server, No Docker: LanceDB Is the Disk-Persisted Vector Store Your RAG Pipeline Has Been Missing

Photo: Pachon in Motion / Pexels

No Server, No Docker: LanceDB Is the Disk-Persisted Vector Store Your RAG Pipeline Has Been Missing

Chris Harper

3 min read

Aug 14, 2026 · 04:19 UTC

AI
Vectors
RAG
Tutorial

LanceDB is an embedded vector database that persists to disk like a file — no server, no Docker, and native hybrid (vector + BM25 keyword) search built in.

You've seen the vector store spectrum already: FAISS is blazing fast but resets on restart; Chroma persists to disk and has a great developer API; pgvector integrates with your existing Postgres. LanceDB fills a distinct gap: fully embedded (no server process), automatic disk persistence in the Apache Lance columnar format, and hybrid search — vector similarity plus BM25 keyword — built in without extra infrastructure.

Think of it as the DuckDB of vector stores: embed it in your process, close the connection, reopen it next week, and your indexed embeddings are exactly where you left them.

Install

pip install lancedb sentence-transformers

Connect and populate

import lancedb
from sentence_transformers import SentenceTransformer

model = SentenceTransformer("all-MiniLM-L6-v2")   # 384-dim, no API key needed

# creates ./rag_db/ on first call; subsequent calls reopen the same store
db = lancedb.connect("./rag_db")

docs = [
    {"id": "doc-1", "text": "LanceDB stores vectors alongside any metadata column", "source": "docs"},
    {"id": "doc-2", "text": "Hybrid search blends vector similarity with BM25 keyword ranking", "source": "blog"},
    {"id": "doc-3", "text": "The Lance columnar format enables fast filtered scans over billions of rows", "source": "blog"},
]

rows = [{**d, "vector": model.encode(d["text"]).tolist()} for d in docs]

table = db.create_table("knowledge", data=rows, mode="overwrite")

Vector search

query = "how does column storage help retrieval?"
q_vec = model.encode(query).tolist()

results = (
    table.search(q_vec)
         .metric("cosine")
         .limit(3)
         .select(["id", "text", "_distance"])
         .to_pandas()
)
print(results)

Hybrid search (vector + BM25)

from lancedb.rerankers import RRFReranker

# build the BM25 full-text index once; persists to disk with the table
table.create_fts_index("text")

results = (
    table.search(
            "columnar storage retrieval",
            query_type="hybrid",
            vector_column_name="vector",
            fts_columns="text",
         )
         .rerank(RRFReranker())   # Reciprocal Rank Fusion blends both lists
         .limit(3)
         .to_pandas()
)

RRF promotes any result that ranks highly in both vector and keyword lists — a result that appears in position 3 of the vector results and position 2 of the BM25 results floats to the top.

Metadata filtering

# SQL WHERE clause on any column, combined with vector search
results = (
    table.search(q_vec)
         .where("source = 'docs'")   # prune before ranking
         .limit(5)
         .to_pandas()
)

ANN index for large tables

Once your table exceeds ~100K rows, add an approximate nearest-neighbor index so search stays sub-millisecond:

table.create_index(metric="cosine", vector_column_name="vector")

Vector store comparison

FAISSChromaLanceDBpgvector
Embedded (no server)YesYesYesNo
Auto disk persistenceNo (manual save)YesYesYes
Hybrid search built-inNoNoYes (BM25 + RRF)Partial
Metadata filterNoYesYes (SQL)Yes (SQL)
Production pathSelf-manageChroma CloudLanceDB CloudManaged Postgres

From local to cloud

When you're ready to scale beyond a single machine, point at LanceDB Cloud — the same table, index, and search code works unchanged, you just swap the connection URI. No rewrite required.

Sources: LanceDB documentation · Full-Text Search — LanceDB docs · Hybrid Search — LanceDB docs · lancedb/lancedb — GitHub