ME

Mem0

Self-hosted memory layer for AI agents and assistants

Self-Hosted AI ★ 65.9k stars Medium setup Apache-2.0

Mem0 is a memory layer that gives LLM applications persistent, personalized recall across sessions. It stores and retrieves user and agent memories through a simple API and can run fully self-hosted with open-source vector stores.

Key features

  • Persistent agent memory
  • Self-hostable REST API
  • Pluggable vector backends
  • User and session scoping

Pros & cons

Strengths

  • Simple memory API
  • Cross-session recall
  • Works with open stores

Trade-offs

  • Vector store required
  • Managed platform upsell

Mem0 replaces

Last reviewed Aug 26, 2026 · 856 words

Every time you call Memory.add() in Mem0, an LLM reads the text, extracts a list of facts, embeds each one, compares them against what is already stored for that user, and decides per fact whether to add, update, delete, or ignore. That is what "memory layer" means in practice: a small extraction pipeline in front of a vector store, not a database. That one sentence explains the catalogue's 1 GB RAM figure (the pipeline, not the data), its "vector store required" con, and why a self-hosted Mem0 is really three services.

Three services, not one

Mem0 the Python library (pip install mem0ai) has no storage of its own. You supply a vector store, an embedding model, and a chat model, and it orchestrates them. The open-source repo also ships a REST server so non-Python apps can use it, but the shape underneath is the same. For a fully local stack I use Ollama for both models and Qdrant for vectors. As a rough estimate, an 8 GB box runs all three plus an 8B-parameter model with room to spare.

from mem0 import Memory

config = {
    "llm": {
        "provider": "ollama",
        "config": {"model": "llama3.1:8b", "ollama_base_url": "http://ollama:11434"},
    },
    "embedder": {
        "provider": "ollama",
        "config": {"model": "nomic-embed-text", "ollama_base_url": "http://ollama:11434"},
    },
    "vector_store": {
        "provider": "qdrant",
        "config": {"host": "qdrant", "port": 6333, "embedding_model_dims": 768},
    },
}

m = Memory.from_config(config)
m.add("I work in metric and never book meetings before 10am", user_id="aidan")
print(m.search("when can I schedule a call?", user_id="aidan"))

The embedding_model_dims line is the one people miss. Mem0 defaults to 1536 dimensions, which matches OpenAI's embeddings; nomic-embed-text produces 768. Get it wrong and Qdrant creates the collection at the wrong size and every insert fails with a dimension mismatch. Delete the collection after fixing the config, because Mem0 will not resize it for you.

Small models make bad librarians

The extraction step is a structured-output task: the model must return clean JSON listing facts and the add/update/delete decisions. Frontier models do this reliably. Local 7B to 8B models mostly do, with occasional junk, and anything smaller degrades fast, producing empty memory lists or duplicating facts it should have merged. If your memories look thin, suspect the model before the code. The pragmatic split I run: a local embedder (cheap, called on every search) and a local 8B model for extraction, with a switch to a hosted model when quality matters more than privacy. The self-hosted AI stack post covers the hardware side of running that 8B model.

Scope everything from the first line

Memories are partitioned by user_id, agent_id, and run_id, and searches only return matches within the scope you pass. There is no global namespace by design. This is the right model, but it means a call with a wrong or missing id quietly writes into a bucket nobody will ever read. Decide your scoping convention (one user_id per human, agent_id per bot persona, run_id per conversation if you want short-lived context) before writing the first memory, because re-scoping later means re-running extraction over everything.

Latency is per write, and it is not free

A search is one embedding call plus a vector query: tens of milliseconds locally. A write is an embedding call plus at least one chat completion, so expect 1 to 5 seconds on a local 8B model with a GPU and longer on CPU-only hardware. Do not put add() on the hot path of a chat response; queue it after the reply is sent. Mem0 also keeps a history of every memory change, which is the audit trail you want when a user asks why the assistant thinks they are vegetarian.

Graph memory (relationships between entities, backed by Neo4j or Memgraph) is optional and I would leave it off until plain vector recall proves insufficient. It doubles the moving parts and the LLM calls per write.

The managed platform is the business model

The catalogue flags a "managed platform upsell", and that is accurate: the hosted service at mem0.ai is where the company makes money, and some features land there first. The Apache-2.0 core has stayed complete enough to build on, and the config-driven design means you are not tied to any single vector or model provider, which is the lock-in that would actually hurt. Pick the store using the vector database guide rather than by default, since swapping later is a full re-embedding job.

What I'd do

Ollama plus Qdrant plus the config above, embedding_model_dims set correctly on day one, a single user_id convention documented in the repo, and add() calls moved off the request path. Run it for two weeks with real conversations before deciding whether extraction quality on a local model is good enough; if it is not, swap only the llm block to a hosted model and keep everything else on your own hardware. Skip graph memory until you can name the query that needs it.

Similar self-hosted ai apps