cachly← All posts
Infrastructure · April 20, 2026

Free, Private Embeddings for Your AI Dev Brain — Powered by Ollama

We now run a local embedding model on our infrastructure. Zero API keys. No prompt data leaves Germany.

The problem we kept hearing

When we launched the Cachly AI Dev Brain — persistent cross-session memory for AI coding assistants — the most common question was:

"Do I need an OpenAI API key just for memory? I'm already using Claude/Copilot/Cursor…"

Short answer: you shouldn't have to. Memory is infrastructure. You don't pay Stripe every time you write to a database. So we fixed it.

The question is a fair one, though, and it deserves more than a slogan. It is worth being precise about what an embedding model is doing in a memory system at all — because once that is clear, both the privacy argument and the cost argument stop being marketing and start being arithmetic.

Why memory needs an embedding model at all

A Brain accumulates lessons. Hundreds of them, eventually thousands. When your assistant is about to touch a deploy script, the useful question is not “which lesson contains the word deploy” — it is “which lesson is about this kind of situation.”

Keyword search cannot answer that.

The lesson that saves you may talk about a restart loop and a healthcheck without ever using the word you typed. Meanwhile a lesson that mentions “deploy” four times may be about something else entirely — a deploy that went fine, in a service you no longer run.

Ranking by word overlap gets both of those backwards, and it does so confidently.

An embedding model turns each piece of text into a list of numbers positioned so that texts about similar things land near each other. Retrieval then becomes a distance question, which a database can answer quickly and consistently.

QUERY"fix deploy"→ 768 numbers →ranked by cosine distance · smaller is closerfix:clickhouse-healthcheck0.11returnedfix:docker-ipv6-bind0.19returneddeploy:api-restart-loop0.28returnednote:team-standup0.74below the cutThe model never sees your question as words at this stage — only as coordinates.
What a recall actually does. The words are gone by the second step — what remains is a position, and the ranking is a distance measurement against everything already stored.

This is also the reason embedding calls are frequent rather than occasional. Every stored lesson needs one when it is written, and every recall needs one when it is asked. A memory system that charges per embedding is a memory system that charges you for remembering.

What we built

We now run nomic-embed-text via Ollama directly on our cachly infrastructure (Hetzner, Germany). Every embedding operation for the AI Brain — index_project, smart_recall, learn_from_attempts, remember_context — goes through this local model.

✅ No OPENAI_API_KEY needed — just your CACHLY_JWT
✅ No prompts, code snippets, or filenames leave Germany
✅ 768-dimension embeddings, cosine similarity search via pgvector
✅ Fast embedding performance (model optimized)
✅ Works even in air-gapped or restricted corporate environments

nomic-embed-text: Why we chose it

ModelDimsSizeMTEB ScoreLicense
nomic-embed-text ★768274 MB62.4Apache 2.0
text-embedding-3-small1536API~62.0OpenAI ToS
text-embedding-ada-0021536API60.9OpenAI ToS
all-MiniLM-L6-v238491 MB56.3Apache 2.0

nomic-embed-text scores on par with OpenAI's best embedding models, is fully open-source, and fits under 300 MB — making it practical to run on existing server infrastructure without a GPU.

One column in that table is a trap worth naming. Dimensions are not quality. A 1536-dimension vector is not twice as good as a 768-dimension one; it is twice as large to store and slower to compare. What matters is the benchmark score, and there the gap is within noise of each other.

The size column matters for a different reason. A model under 300 MB runs on ordinary server CPUs, which is what makes “no API key” economically possible in the first place. A model that needed a GPU would simply move the bill rather than remove it.

How it works under the hood

MCP Tool Call: smart_recall("fix deploy")
       ↓
cachly MCP Server (npm)
       ↓ HTTP
cachly API (Go) — POST /api/v1/semantic/search
       ↓
EmbedHandler.Embed("fix deploy")
       ↓ HTTP (internal Docker network)
Ollama: POST http://ollama:11434/api/embeddings
       ↓
nomic-embed-text → 768-dim float32 vector
       ↓
pgvector: SELECT ... ORDER BY embedding <=> $1 LIMIT 10
       ↓
Top-k lessons returned to your AI assistant
ONE NODE, GERMANY — DEFAULT PATHMCP servernpmcachly APIGoOllamanomic-embed-textpgvectortop-kexternal provideronly if you set CACHLY_EMBED_PROVIDERThe dashed amber path is opt-in. It is the only route on which your text leaves the node.Nothing else changes — the same call, the same store, a different hop in the middle.
Every hop in the default path stays on one machine. That is the whole privacy claim, drawn out: it is not a policy about what we promise not to look at, it is a route your text never takes.

Everything runs on a single Hetzner CPX32 node in Germany. The Ollama container uses max 700 MB RAM and stays idle at ~62 MB between requests.

Those two numbers are the interesting part of the whole setup. A service that idles at 62 MB can share a node with everything else instead of demanding its own. That is what turns “run your own embeddings” from an architecture diagram into a line item that rounds to nothing.

Bring your own model (optional)

If you'd rather use your own embedding provider, set these in your MCP config:

{
  "env": {
    "CACHLY_JWT": "your-jwt",
    "CACHLY_BRAIN_INSTANCE_ID": "your-instance-id",
    "CACHLY_EMBED_PROVIDER": "openai",
    "OPENAI_API_KEY": "sk-..."
  }
}

The Brain falls back automatically to your provider when CACHLY_EMBED_PROVIDER is set. Otherwise it uses our hosted nomic-embed-text instance.

The one decision that is expensive to reverse

Worth saying plainly, because it is the kind of thing that is obvious in hindsight and costly in practice: vectors produced by one model cannot be compared with vectors produced by another.

They are not merely on different scales. They are coordinates in different spaces, built by different training runs. Comparing them is not inaccurate — it is meaningless.

STORED WITH MODEL A768-dim · nomic-embed-textQUERIED WITH MODEL B1536-dim · other providerNot a smaller result set. No result set — the coordinates mean different things.Switching provider means re-indexing everything that was ever stored. Decide once, early.
Why this is a one-way door in practice. Switching the embedding provider does not degrade recall quality; it invalidates the entire stored index until everything is embedded again with the new model.

So the practical advice is unglamorous. If you have a policy reason to run embeddings through your own provider, set it up before you store much. If you do not, the default costs you nothing and keeps the text on one machine.

Either choice is defensible. Changing your mind after a year of accumulated lessons is the expensive path, and nothing in the tooling will stop you from taking it by accident.

Restricted and air-gapped environments

The bullet list above claims this works where outbound API calls are blocked. That claim is worth unpacking, because it is the case where hosted embeddings fail outright rather than merely costing money.

In a network that cannot reach a third-party API, a memory system built on hosted embeddings does not degrade gracefully. It stops. Every write and every recall depends on a round trip that will never complete.

A model that runs beside the database has no such dependency. The same 274 MB that made it cheap on a shared node is what makes it viable behind a firewall — it can simply be present.

An embedding service is now something you operate

Honesty about the trade: removing an API key does not remove a dependency. It converts an external one into an internal one.

A hosted provider fails in ways you read about on a status page. A local container fails in ways you find out about yourself, usually because something upstream started returning results that look thin rather than results that look broken.

So the thing worth watching is not uptime. It is whether embedding calls are still succeeding at all — a recall that quietly falls back to keyword matching still returns rows, and rows look like success from every angle except relevance.

What this does not solve

Local embeddings cover the memory layer, and only the memory layer. Your coding assistant still sends your prompts to whichever model you use — Claude, Copilot, Cursor, anything else. That traffic is not affected by any of the above.

What changes is the part that used to be an extra, avoidable disclosure: the text of your lessons, your cached context, and your recall queries. Those now stay put by default, and they were the hardest part to justify sending anywhere in the first place.

It is a narrower claim than “private AI,” and it is one we can actually draw on a diagram.

Narrow claims have a useful property: you can check them. Point a packet capture at the node during a recall and see for yourself which hosts it talks to. A promise that survives that test is worth more than a broad one that cannot be tested at all.

Get started

npx @cachly-dev/mcp-server@latest autopilot

No OPENAI_API_KEY needed. Just your CACHLY_JWT.

cachly is a persistent AI Brain for developers — memory shared across Claude Code, Cursor, GitHub Copilot & Windsurf simultaneously. Auto-detects every editor. Bootstraps from your git history. 122 MCP tools. Free tier, EU servers, no credit card.

Your AI is forgetting everything right now.

Every session starts blank. Every bug re-discovered. Every deploy procedure re-explained. cachly fixes that in 30 seconds — your AI remembers every lesson, every fix, every teammate's hard-won knowledge. Forever.

Four ways to install

Claude Code
/plugin marketplace add cachly-dev/cachly-mcp
/plugin install cachly-brain@cachly
Any MCP editor
npx @cachly-dev/mcp-server@latest autopilot
VS Code
JetBrains
🇪🇺 EU servers · GDPR-compliant🆓 Free tier — forever, no credit card⚡ 30-second setup via npx🔌 Claude Code · Cursor · Copilot · Windsurf