---
title: "Enabling semantic search"
description: "Optional vector search: a heavy native stack, the VECTOR_SEARCH switch, model tiers, and a fallback to full-text search with no loss of function."
---

# Enabling semantic search

Full-text search (FTS) in Notarium works **always, with no setup**. Semantic (vector) search is an optional capability: it adds hybrid ranking by meaning, but it pulls in a heavy native stack (`onnxruntime` + `sqlite-vec`, ~660 MB on disk) and a local embedding model that downloads when first enabled (~600 MB on disk and hundreds of MB of RAM). That's why it's off by default and turned on deliberately. For how search works under the hood, see [Concepts: search](/docs/concepts/search/).

## Two independent switches

It helps to keep two levels apart — installation and runtime:

1. **Installation** — whether the native vector stack is present in `node_modules`. **The Docker image always ships it**, so turning it on inside the container requires no rebuild. When running from source, the default `make deps` does **not** install it (a local `node_modules` is ~660 MB lighter) — for local vector work you need `make deps-vector`.
2. **Runtime** — the `VECTOR_SEARCH` variable. In the published image it defaults to `off`.

For semantic search in Docker, flipping the runtime switch is enough — the stack is already in place.

## Turning it on

```bash
docker run -d --name notarium \
  -p 3000:3000 \
  -v notarium-data:/data \
  -e VECTOR_SEARCH=on \
  docouno/notarium:latest
```

(A single `/data` volume holds all state — the metadata database, indexes, your notes, export artifacts; the same as in a plain run, see [Install](/docs/self-hosting/install/). Here it only gains `VECTOR_SEARCH=on`.)

When first enabled, the default model (bge-m3) downloads (~600 MB on disk) and uses hundreds of MB of RAM. Indexing runs in the background: full-text search is available right away, while the vectors catch up.

## Model tiers

The model is chosen by the pair `EMBED_MODEL` + `EMBED_DIMENSIONS` (the dimensionality **must** match the model) — it's a runtime setting on a single image, not a separate build:

| Tier | Variables | RAM | When |
|---|---|---|---|
| **off** | `VECTOR_SEARCH=off` | 0 | A low-powered machine, or keyword search is enough. The image default. |
| **compact** | `VECTOR_SEARCH=on`, `EMBED_MODEL=Xenova/multilingual-e5-small`, `EMBED_DIMENSIONS=384` | ~120 MB | Homelab, a small VPS. |
| **full** | `VECTOR_SEARCH=on` (defaults: bge-m3, 1024) | ~600 MB | A powerful machine; 100+ languages, long context. |

> [!warning] Asymmetric e5 models
> The compact tier (e5) requires the prefixes `EMBED_QUERY_PREFIX="query: "` and `EMBED_PASSAGE_PREFIX="passage: "` — forgetting them silently degrades search quality. For the symmetric bge-m3, the reverse holds: you **shouldn't** set the prefixes at all.

## Resources on a tight machine

On a host without swap and ~6 GB of RAM, the initial bge-m3 indexing can hit the memory ceiling. Two levers: use the compact tier (e5-small), or set `EMBED_CPU_MEM_ARENA=off` — this keeps consumption around ~1.9 GB at the cost of a small slowdown. For the full list of embedding parameters, see the [Reference](/docs/reference/environment-variables/).

## Falling back to full-text search

If `VECTOR_SEARCH=off`, or the native stack fails to load for any reason, search **keeps working over full text** — with no error, and the results look the same. Semantic search is an extra ranking channel on top of the always-on FTS, not a hard dependency. An instance without vectors is a fully working instance.
