Providers
- OpenAI-compatible (self-hosted path)
- Azure OpenAI
Anything speaking the OpenAI With a local model, the text of a field note never leaves your network.
/v1/embeddings shape: Ollama, vLLM, LiteLLM, LocalAI, or
api.openai.com itself.Model choices
Any model works as long as
EMBEDDING_DIM matches its real output width and the
posts.embedding column width. All three must agree.
Failure behaviour
The embedding client never raises. On a missing configuration, an HTTP error, a timeout, or a malformed body it returnsNone, and the two call sites diverge:
Every failed call is logged with its reason:
Keeping the model warm
A local model is slow on a cold start — the first call after a pod boots can take seconds while weights load, which blows the 2-second query timeout and produces503s that look
like an outage.
Raise the query timeout
EMBEDDING_TIMEOUT_QUERY=5.0 is a reasonable self-hosted value. The cost of waiting is
lower than the cost of a spurious “unavailable”.Keep weights resident
For Ollama,
OLLAMA_KEEP_ALIVE=-1 holds the model in memory indefinitely. Pair it with
a postStart pull and a memory limit that actually fits the model.Capacity
The API is I/O-bound and cheap to scale; the embedding backend is the component that actually runs out. Size it deliberately, because nothing else in the system will tell you it is the bottleneck — searches simply get slower until they crossEMBEDDING_TIMEOUT_QUERY and start returning 503.
Measure it on your hardware
Embedding throughput swings by an order of magnitude between GPU, Apple Silicon with Metal, and a CPU-only pod. Measure yours:nomic-embed-text, we measure p50 14 ms and ~100 req/s
flat from concurrency 4 to 16. A CPU-only pod on a shared node is very substantially slower.
Turning the measurement into replicas
Each search costs one embedding call on the request path; each note costs one off it. So:POST /v1/search
p95 approaching EMBEDDING_TIMEOUT_QUERY means you are about to serve 503s, and a rising
embedding IS NULL count means the background embed is losing against the write rate.
Changing model or dimension
1
Stop writes
Scale the API to zero, or accept that notes written during the migration will need a
backfill afterwards.
2
Retype the column and rebuild the index
pgvector rejects a vector whose width disagrees with its column, so the column must be
retyped before any note is embedded with the new model.
3
Update configuration
EMBEDDING_MODEL and EMBEDDING_DIM together, and roll the API.4
Re-embed everything
5
Confirm
missing reaches zero.