LanceDB is a vector database that runs inside your Python process. No server to start, no container to babysit, no port to open. You pip install it, point it at a directory, and it stores everything as columnar files on disk. That makes it the fastest way to add semantic search to a script or an app, and it still handles a million vectors on a single machine, as the benchmark below shows. Built and run on Ubuntu 24.04 in September 2026, on LanceDB 0.38.
This is the short path: install LanceDB, embed some text locally, run a semantic query, then throw a million vectors at it to see where it lands. If you’re new to the idea, the difference between vector search and keyword search is covered separately. Straight to it.
Install
Two packages: LanceDB and an embedding model. fastembed runs on CPU with no PyTorch and no GPU, which keeps this quick:
python3 -m venv venv && source venv/bin/activate
pip install lancedb fastembed
That’s the whole install. There is nothing to run in the background.
Embed and store
Turn text into vectors with fastembed, then drop them into a LanceDB table. The table lives in the directory you connect to, so it persists across runs on its own:
import lancedb
from fastembed import TextEmbedding
model = TextEmbedding(model_name="BAAI/bge-small-en-v1.5")
docs = [
"Kubernetes schedules containers across a cluster of nodes.",
"PostgreSQL is a relational database with strong ACID guarantees.",
"Prometheus scrapes metrics endpoints and stores time series.",
"Nginx can act as a reverse proxy and load balancer.",
"ZFS is a copy-on-write filesystem with snapshots and checksums.",
"Ansible automates server configuration over SSH without agents.",
]
vecs = list(model.embed(docs))
db = lancedb.connect("./lancedb-demo")
rows = [{"text": t, "vector": v.tolist()} for t, v in zip(docs, vecs)]
tbl = db.create_table("notes", data=rows, mode="overwrite")
print("rows:", tbl.count_rows(), "| vector dim:", len(vecs[0]))
The model returns 384-dimension vectors. LanceDB infers the schema from the first rows, so there is no separate “create collection with dimension N” step.
Search
Embed the query the same way, then hand the vector to search. Cosine distance is the right metric for these embeddings:
for q in ["how do I watch server metrics", "database with transactions"]:
qv = list(model.embed([q]))[0].tolist()
print("query:", repr(q))
for row in tbl.search(qv).distance_type("cosine").limit(2).to_list():
print(round(row["_distance"], 3), row["text"])
Each query returns the line closest in meaning. The metrics question surfaces the Prometheus line, and the transactions question surfaces PostgreSQL:

That is a working semantic search in about a dozen lines. This is the exact backend you’d put behind a local RAG pipeline, swapping fastembed for whatever embedding model you already run, including one served by a local Ollama.
Scale it
“Embedded” sounds like a toy. It isn’t. Loaded with the 1.18M-vector glove-100 set, LanceDB ingested the lot in one second and built an approximate index in under half a minute, all on four CPU cores with no GPU:
tbl = db.create_table("glove", data=arrow_table) # 1,183,514 vectors
tbl.create_index(metric="cosine", index_type="IVF_PQ",
num_partitions=1024, num_sub_vectors=25)
hits = (tbl.search(query).distance_type("cosine")
.nprobes(100).refine_factor(10).limit(10).to_list())
The index is IVF_PQ: it clusters the vectors and stores them product-quantized, which is lossy but tiny. Recall is a dial. More nprobes (clusters searched) and a higher refine_factor (exact re-ranking of candidates) trade latency for accuracy:

At the top setting the queries returned recall@10 of 0.91 with a p95 near 6 ms. The number that stands out is on disk: the whole million-vector index came to 488 MiB. That is mostly the raw vectors themselves, since a million 100-dimension float32 vectors is about 451 MiB, plus the quantized codes and cluster centroids. It lands smaller than a running HNSW server holds the same data because IVF_PQ skips the neighbor graph HNSW must keep, not because it throws the vectors away. The full vectors stay on disk so the refine step can re-rank against them, and the trade is recall you tune up rather than get for free.
When to reach for it
LanceDB wins when the vectors belong inside one application: a desktop app, a notebook, a single service, a CI job that needs search without standing up infrastructure. It ships in your process and the data is just files you can copy or commit to object storage.
Reach for a server instead when many services query the same index concurrently, when you need live writes at scale with role-based access, or when you want HNSW recall without hand-tuning quantization. That is the territory of running Qdrant or keeping vectors next to relational data with pgvector in PostgreSQL. For a script that needs to search a million embeddings tomorrow morning, LanceDB is already done. That’s it.