This guide is for the people who run, deploy, extend, and operate Mem0: platform engineers, backend developers, SREs, and architects. It covers the system architecture, the full configuration reference, deployment of the self-hosted REST server and OpenMemory, security, operations, and the internals you'd need to extend Mem0 or reason about its behavior under load.
If you're looking for "how do I store and search a memory," start with the companion Mem0 User Guide. This document assumes you already know the verbs and focuses on everything around them.
Mem0 is a memory layer for AI agents and applications. It is distributed as one package (mem0ai) that operates in two modes — a managed Platform (MemoryClient, hosted at api.mem0.ai) and a self-managed Open Source engine (Memory) you wire to your own LLM, embedder, and vector store. Around that core sit a self-hostable REST server, the OpenMemory MCP server, a CLI, and framework integrations.
Table of Contents
- Executive Summary
- System Architecture
- Repository Structure
- The Memory Engine: Design & Data Flow
- Requirements
- Configuration Reference
- Provider Catalog
- Deploying the Self-Hosted REST Server
- Deploying OpenMemory
- Security
- Operations
- Observability & Telemetry
- Performance & Scaling
- Module Reference
- Extending Mem0
- Testing & Quality
- Known Issues & Improvement Opportunities
- Troubleshooting (Operator)
- Quick Reference
1. Executive Summary
What it is. Mem0 gives AI applications long-term memory: it extracts durable facts from conversations with an LLM, stores them as searchable vectors, and retrieves the relevant ones on demand. It replaces the anti-pattern of stuffing entire transcripts into every prompt.
How it's deployed. Three operating models, chosen per deployment:
| Model | Component | Where memory + compute live | Operator burden |
|---|---|---|---|
| Managed | MemoryClient → api.mem0.ai |
Mem0 cloud | None |
| Library (embedded) | Memory in your process |
Your vector store + your LLM keys | You own the vector store |
| Self-hosted service | server/ FastAPI + dashboard |
Your Postgres/pgvector + your LLM keys | You own the full stack |
| Personal server | openmemory/ Docker stack + MCP |
Your machine (Dockerized) | Docker host |
Key architectural properties.
- Pluggable everything. LLM, embedder, vector store, and reranker are all selected by a
providerstring and instantiated through factories. Swapping Qdrant for pgvector or OpenAI for Ollama is a config change, not a code change. - Anti-hallucination extraction. A carefully engineered extraction prompt (the "additive extraction" prompt) governs what becomes a memory: preserve proper nouns/numbers/dates, ground relative time to absolute, never fabricate, deduplicate against existing memories.
- Hybrid retrieval. Search blends semantic similarity, BM25 keyword matching (spaCy-lemmatized), and entity-overlap boosting into a single normalized score, with optional reranking.
- Filesystem/SQLite-light footprint for OSS. The embedded library needs only a vector store and a small SQLite history DB (
~/.mem0/history.db).
Primary risks to manage. LLM/provider availability and cost (every add is an LLM call), vector-store sizing, secret handling for the many provider keys, and — for the self-hosted server — authentication configuration (it ships auth-on by default and will 401 everything until an admin or key is configured).
2. System Architecture
2.1 The two faces of the engine
┌──────────────────────────────────────────────────────────────────────┐
│ Your Application │
│ │
│ from mem0 import Memory from mem0 import MemoryClient │
│ (Open Source) (Platform) │
└───────────────┬───────────────────────────────┬───────────────────────┘
│ │
│ in-process │ HTTPS Authorization: Token m0-…
▼ ▼
┌───────────────────────────────┐ ┌────────────────────────────────────┐
│ Memory engine │ │ api.mem0.ai (managed) │
│ │ │ /v3/memories/add, /search, … │
│ ┌─────────┐ ┌────────────┐ │ │ graph memory, exports, webhooks, │
│ │ LLM │ │ Embedder │ │ │ projects, feedback, summaries │
│ └─────────┘ └────────────┘ │ └────────────────────────────────────┘
│ ┌─────────────────────────┐ │
│ │ Vector store │ │
│ │ (Qdrant / pgvector /…) │ │
│ └─────────────────────────┘ │
│ ┌─────────┐ ┌────────────┐ │
│ │ History │ │ Entity │ │
│ │ SQLite │ │ store │ │
│ └─────────┘ └────────────┘ │
└───────────────────────────────┘
2.2 Self-hosted REST service
┌─────────────────────────────────────────────┐
HTTP clients │ server/ (FastAPI) │
──────────────▶ Auth: JWT / X-API-Key / ADMIN_API_KEY │
X-API-Key / │ Routers: /auth /api-keys /entities /requests│
Bearer JWT │ Memory: /memories /search /configure ... │
│ │
│ uses ──► mem0.Memory (one shared instance)│
└───────┬───────────────────────┬───────────────┘
│ │
┌──────▼──────┐ ┌──────▼───────────┐
│ Postgres │ │ pgvector (mem) │
│ (app: users,│ │ + SQLite history│
│ api keys) │ │ │
└─────────────┘ └──────────────────┘
▲
┌──────┴───────┐
│ Dashboard │ Next.js admin UI (server/dashboard)
└──────────────┘
The REST server holds one shared Memory instance (guarded by a reentrant lock) configured from a default config plus DB-persisted overrides. App-level data (users, API keys, request logs) lives in a separate Postgres database (mem0_app); memories live in pgvector; history lives in SQLite.
2.3 OpenMemory
MCP client (Claude Desktop / Cursor) Browser
│ SSE: /{client}/sse/{user_id} │ http://localhost:3000
▼ ▼
┌────────────────────────────┐ ┌──────────────────┐
│ openmemory/api (FastAPI) │◀────────│ openmemory/ui │
│ MCP server + REST + MCP │ │ (Next.js) │
│ uses mem0ai library │ └──────────────────┘
└──────────┬─────────────────┘
▼
Vector store (Qdrant default) + SQLite app DB
OpenMemory is a fully self-contained Docker stack: a FastAPI backend exposing both REST and an MCP (Model Context Protocol) SSE endpoint, plus a Next.js dashboard. It uses the mem0ai library internally.
3. Repository Structure
The repo is a monorepo containing the library plus every surface built on it.
| Path | Component | Language | Published as |
|---|---|---|---|
mem0/ |
Core Python library (engine + Platform client) | Python | mem0ai (PyPI) |
mem0-ts/ |
TypeScript SDK (Platform + OSS impl) | TypeScript | mem0ai (npm) |
mem0-ts/src/community/ |
LangChain integration | TypeScript | @mem0/community |
server/ |
Self-hosted REST API (FastAPI) | Python | Docker image |
server/dashboard/ |
Admin dashboard for the REST server | Next.js | — |
openmemory/api/ |
OpenMemory backend (REST + MCP) | Python | mem0/openmemory-mcp |
openmemory/ui/ |
OpenMemory dashboard | Next.js | mem0/openmemory-ui |
cli/node/ |
Official CLI (mem0 command) |
TypeScript | @mem0/cli |
cli/python/ |
Python CLI | Python | — |
vercel-ai-sdk/ |
Vercel AI SDK provider | TypeScript | @mem0/vercel-ai-provider |
openclaw/ |
OpenClaw memory plugin | TypeScript | @mem0/openclaw-mem0 |
mem0-plugin/ |
Claude Code plugin + skills | Python/MD | — |
skills/ |
Claude Agent skills | Markdown | — |
evaluation/ |
Benchmark/eval harness | Python | — |
examples/ |
Runnable demos across frameworks | mixed | — |
cookbooks/ |
Worked recipes | Python | — |
docs/ |
Documentation site (Mintlify source) | MDX | docs.mem0.ai |
tests/ |
Python test suite | Python | — |
Core Python package (mem0/)
mem0/
__init__.py exports Memory, AsyncMemory, MemoryClient, AsyncMemoryClient
memory/main.py Memory & AsyncMemory — the engine
memory/storage.py SQLiteManager (history DB)
memory/setup.py ~/.mem0 dir, anon IDs, config setup
memory/telemetry.py PostHog anonymous telemetry
client/main.py MemoryClient & AsyncMemoryClient (Platform HTTP client)
client/project.py Project manager (instructions, categories)
configs/ Pydantic config models (base, prompts, enums, per-provider)
llms/ 18 LLM provider implementations
embeddings/ 11 embedder implementations
vector_stores/ 24 vector-store implementations
reranker/ 5 reranker implementations
utils/factory.py Provider factories (the plug-in registry)
utils/entity_extraction.py / scoring.py / lemmatization.py hybrid-search internals
exceptions.py Structured exception hierarchy
proxy/main.py Mem0 OpenAI-compatible LLM proxy
4. The Memory Engine: Design & Data Flow
4.1 Memory construction
Memory(config: MemoryConfig) instantiates four collaborators via factories:
embedding_model—EmbedderFactory.create(provider, config, vector_config)vector_store—VectorStoreFactory.create(provider, config)llm—LlmFactory.create(provider, config)db—SQLiteManager(history_db_path)for the change-history audit trailreranker— optional,RerankerFactory.create(...)whenconfig.rerankeris setentity_store— a lazily-initialized second vector collection ({collection}_entities) for entity-boost retrieval
Memory.from_config(config_dict) validates a plain dict against the MemoryConfig Pydantic model and returns a constructed instance — this is the entry point the REST server and most integrations use.
4.2 The add() pipeline (inference path)
When infer=True, add() runs a phased batch pipeline (V3):
- Phase 0 — context. Pull the last ~10 messages for the session scope from SQLite to resolve references.
- Phase 1 — retrieve existing. Embed the incoming messages, search the vector store (top 10) for related memories, and map their UUIDs to small integer IDs (an anti-hallucination trick — the LLM sees
"0","1",…not raw UUIDs). - Phase 2 — extract. A single LLM call with the additive extraction system prompt produces self-contained facts. If the scope is agent-only, an agent-context suffix re-frames extraction from the agent's perspective.
- Phase 3 — embed. Batch-embed all extracted fact texts.
- Phases 4–5 — dedup. Skip facts whose MD5 content hash already exists (against both stored memories and within the batch).
- Phase 6 — persist. Batch-insert vectors + payloads; write batch history records (
ADD). - Phase 7 — entity linking. Extract named entities (spaCy), globally dedup them, batch-embed, search the entity store, then update existing entity records'
linked_memory_idsor insert new ones. - Phase 8 — save + telemetry. Persist the raw messages for future context and emit an anonymous telemetry event.
When infer=False, the pipeline is skipped: each non-system message is embedded and stored verbatim as its own memory.
The extraction prompt is the engine's "core IP." It is exhaustive about what to extract (every distinct topic, incidental facts, shared reference material, multi-speaker attribution) and strict about integrity (no fabrication, no echo extraction, no detail contamination, preserve exact proper nouns/numbers/dates, ground relative time to the observation date). This is what keeps generated memories faithful to source.
4.3 The search() pipeline (hybrid retrieval)
- Preprocess. Lemmatize the query (spaCy) and extract query entities.
- Embed the query.
- Semantic search the vector store, over-fetching (
max(top_k*4, 60)) for a scoring pool. - Keyword search (BM25) over lemmatized text, if the store supports it.
- BM25 normalization. Raw BM25 scores are squashed to
[0,1]via a query-length-adaptive logistic sigmoid. - Entity boosts. Query entities are matched against the entity store; linked memories get a boost (attenuated when an entity links to many memories).
- Score & rank.
combined = (semantic + bm25 + entity_boost) / max_possible, with the semantic score gated bythresholdbefore combining.max_possibleadapts to which signals are present (1.0 → 2.5). - Format & (optional) rerank. Build
MemoryItemresults; ifrerank=Trueand a reranker is configured, re-order the top results.
4.4 Data model
A stored memory's vector-store payload carries: data (the memory text), hash, created_at, updated_at, text_lemmatized, the session IDs (user_id/agent_id/run_id), optional actor_id/role/attributed_to, and any user metadata. Results are shaped into a MemoryItem (id, memory, hash, metadata, score, created_at, updated_at) with the session IDs promoted to top-level fields.
The history SQLite table records every ADD/UPDATE/DELETE with old/new text, timestamps, actor/role, and an is_deleted flag.
5. Requirements
Library (embedded)
| Component | Requirement |
|---|---|
| Python | 3.10+ |
| LLM provider | An API key for at least one supported LLM (default: OpenAI) |
| Embedder | An API key/endpoint for an embedder (default: OpenAI) |
| Vector store | One supported store reachable from the process (default: local Qdrant) |
| NLP (recommended) | pip install "mem0ai[nlp]" + en_core_web_sm spaCy model for full hybrid search |
Self-hosted REST server (server/)
| Component | Requirement |
|---|---|
| Python | 3.10+ (see server/requirements.txt) |
| Postgres | With pgvector extension for memory storage; a separate mem0_app DB for app data |
| Secrets | JWT_SECRET (required unless AUTH_DISABLED), an LLM key (OPENAI_API_KEY by default) |
| Bundled providers | LLM: OpenAI, Anthropic, Gemini · Embedder: OpenAI, Gemini |
Key server dependencies (pinned): fastapi==0.115.8, uvicorn==0.34.0, pydantic[email]==2.10.4, mem0ai>=0.1.48, psycopg>=3.2.8, sqlalchemy>=2.0, alembic, passlib[bcrypt], python-jose[cryptography], slowapi, posthog.
OpenMemory (openmemory/)
| Component | Requirement |
|---|---|
| Docker + Docker Compose V2 | Required |
OPENAI_API_KEY |
Required |
| Vector store | Selectable; Qdrant default (containerized) |
OpenMemory API deps include fastapi, uvicorn, sqlalchemy, alembic, mem0ai>=0.1.92, mcp[cli]>=1.25.4.
6. Configuration Reference
6.1 MemoryConfig (top-level)
Memory.from_config() accepts a dict matching this Pydantic model:
| Field | Type | Default | Purpose |
|---|---|---|---|
vector_store |
{provider, config} |
Qdrant | Where vectors are stored |
llm |
{provider, config} |
OpenAI | Fact extraction model |
embedder |
{provider, config} |
OpenAI | Embedding model |
reranker |
{provider, config} or null |
null |
Optional reranker |
history_db_path |
str | ~/.mem0/history.db |
SQLite audit DB path |
version |
str | "v1.1" |
API behavior version |
custom_instructions |
str or null |
null |
Steer fact extraction |
6.2 Config shape
Every component follows the same { "provider": "...", "config": { ... } } shape:
config = {
"version": "v1.1",
"llm": {
"provider": "openai",
"config": {"model": "gpt-4o-mini", "temperature": 0.1, "api_key": "sk-..."},
},
"embedder": {
"provider": "openai",
"config": {"model": "text-embedding-3-small"},
},
"vector_store": {
"provider": "qdrant",
"config": {"collection_name": "memories", "host": "localhost", "port": 6333},
},
"reranker": {
"provider": "cohere",
"config": {"model": "rerank-english-v3.0"},
},
"custom_instructions": "Prioritize travel and dietary facts.",
}
6.3 Common LLM config keys
The base LLM config exposes: model, temperature, api_key, max_tokens, top_p, top_k, enable_vision, vision_details, plus provider-specific keys (Azure deployment/endpoint/version, API-base overrides, etc.).
Tip
enable_vision: trueon the LLM config turns on multimodal message parsing inadd(), so image content in messages is described and remembered. Pair it with a vision-capable model.
6.4 Provider credentials via environment variables
Most providers read their key from the environment if not passed in config. A non-exhaustive map (from source):
| Provider area | Environment variables |
|---|---|
| OpenAI | OPENAI_API_KEY, OPENAI_BASE_URL, OPENAI_API_BASE |
| OpenRouter | OPENROUTER_API_KEY, OPENROUTER_API_BASE |
| Anthropic | ANTHROPIC_API_KEY |
| Gemini / Google | GOOGLE_API_KEY, GOOGLE_CLOUD_PROJECT, GOOGLE_APPLICATION_CREDENTIALS |
| Azure OpenAI (LLM) | LLM_AZURE_OPENAI_API_KEY, LLM_AZURE_DEPLOYMENT, LLM_AZURE_ENDPOINT, LLM_AZURE_API_VERSION |
| Azure OpenAI (embed) | EMBEDDING_AZURE_OPENAI_API_KEY, EMBEDDING_AZURE_DEPLOYMENT, EMBEDDING_AZURE_ENDPOINT, EMBEDDING_AZURE_API_VERSION |
| DeepSeek | DEEPSEEK_API_KEY, DEEPSEEK_API_BASE |
| Together | TOGETHER_API_KEY |
| Groq | GROQ_API_KEY |
| xAI | XAI_API_KEY, XAI_API_BASE |
| MiniMax | MINIMAX_API_KEY, MINIMAX_API_BASE |
| Sarvam | SARVAM_API_KEY, SARVAM_API_BASE |
| vLLM | VLLM_API_KEY, VLLM_BASE_URL |
| AWS Bedrock | AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN |
| Cohere reranker | COHERE_API_KEY |
| ZeroEntropy reranker | ZERO_ENTROPY_API_KEY |
| Pinecone | PINECONE_API_KEY |
| Turbopuffer | TURBOPUFFER_API_KEY |
| Engine | MEM0_DIR, MEM0_API_KEY, MEM0_TELEMETRY |
Important
Mem0's telemetry/redaction logic treats any config field named
api_key,*_secret,*_token,*_password,credentials, etc. as sensitive and redacts it before any telemetry capture, while preserving runtime auth objects (http_auth,connection_class,ssl_context). Don't rename secret-bearing fields away from these conventions or they may leak into telemetry payloads.
7. Provider Catalog
These lists come directly from the factory registries in mem0/utils/factory.py — the authoritative source of "what's supported."
LLM providers (LlmFactory)
openai, openai_structured, azure_openai, azure_openai_structured, anthropic, gemini, groq, together, aws_bedrock, litellm, deepseek, minimax, xai, sarvam, ollama, lmstudio, vllm, langchain.
Embedder providers (EmbedderFactory)
openai, azure_openai, ollama, huggingface, gemini, vertexai, together, lmstudio, aws_bedrock, fastembed, langchain. (Plus a MockEmbeddings used automatically when the Upstash vector store provides its own embeddings.)
Vector store providers (VectorStoreFactory)
qdrant, chroma, pgvector, milvus, pinecone, redis, valkey, weaviate, faiss, elasticsearch, opensearch, mongodb, supabase, azure_ai_search, azure_mysql, databricks, vertex_ai_vector_search, upstash_vector, s3_vectors, cassandra, baidu, neptune (Neptune Analytics), turbopuffer.
Reranker providers (RerankerFactory)
cohere, sentence_transformer, zero_entropy, llm_reranker, huggingface.
Note
Providers are imported lazily via
load_class(). A provider's heavy client library (e.g.qdrant-client,pymilvus,weaviate-client) is only imported when you actually select it, so installingmem0aidoesn't pull every vector-store SDK. Install the extra you need for your chosen store.
8. Deploying the Self-Hosted REST Server
The server/ directory is a FastAPI app titled "Mem0 REST APIs" that wraps a single shared Memory instance and adds auth, a dashboard, and per-user API keys.
8.1 Default runtime config
On boot the server constructs this DEFAULT_CONFIG (overridable via the /configure endpoint, persisted to the settings table):
{
"version": "v1.1",
"vector_store": {"provider": "pgvector",
"config": {"host": POSTGRES_HOST, "port": POSTGRES_PORT,
"dbname": POSTGRES_DB, "user": POSTGRES_USER,
"password": POSTGRES_PASSWORD,
"collection_name": POSTGRES_COLLECTION_NAME}},
"llm": {"provider": "openai",
"config": {"api_key": OPENAI_API_KEY, "temperature": 0.2,
"model": MEM0_DEFAULT_LLM_MODEL}}, # default gpt-4.1-nano-2025-04-14
"embedder": {"provider": "openai",
"config": {"api_key": OPENAI_API_KEY,
"model": MEM0_DEFAULT_EMBEDDER_MODEL}}, # text-embedding-3-small
"history_db_path": HISTORY_DB_PATH, # default /app/history/history.db
}
8.2 Environment variables
| Variable | Default | Purpose |
|---|---|---|
JWT_SECRET |
— (required unless AUTH_DISABLED) |
Signs access/refresh JWTs (openssl rand -base64 48) |
ADMIN_API_KEY |
— | Legacy single admin key; sent as X-API-Key |
AUTH_DISABLED |
false |
Open all endpoints — local dev only |
OPENAI_API_KEY |
— | Default LLM + embedder key |
POSTGRES_HOST / PORT / USER / PASSWORD |
postgres/5432/postgres/postgres |
pgvector connection |
POSTGRES_DB |
postgres |
DB holding the memory collection |
APP_DB_NAME |
mem0_app |
DB holding users/api-keys/logs |
POSTGRES_COLLECTION_NAME |
memories |
pgvector collection name |
HISTORY_DB_PATH |
/app/history/history.db |
SQLite history DB |
MEM0_DEFAULT_LLM_MODEL |
gpt-4.1-nano-2025-04-14 |
Default extraction model |
MEM0_DEFAULT_EMBEDDER_MODEL |
text-embedding-3-small |
Default embedder |
DASHBOARD_URL |
http://localhost:3000 |
CORS allow-origin |
MEM0_TELEMETRY |
true |
Anonymous telemetry toggle |
MEM0_TELEMETRY_STATE_PATH |
/app/history/telemetry.json |
Telemetry state file |
8.3 Endpoints
Memory (all require auth unless AUTH_DISABLED):
| Method | Path | Handler | Notes |
|---|---|---|---|
| GET | /configure |
get_config |
Returns current config (secrets redacted) |
| GET | /configure/providers |
list_bundled_providers |
Lists bundled LLM/embedder providers |
| POST | /configure |
set_config |
Update config (validates bundled providers) |
| POST | /generate-instructions |
generate_instructions |
LLM-generates custom instructions from a use case |
| POST | /memories |
add_memory |
Requires one of user/agent/run id |
| GET | /memories |
get_all_memories |
Lists all if no id supplied |
| GET | /memories/{id} |
get_memory |
|
| POST | /search |
search_memories |
Pass ids inside filters (top-level deprecated) |
| PUT | /memories/{id} |
update_memory |
|
| GET | /memories/{id}/history |
memory_history |
|
| DELETE | /memories/{id} |
delete_memory |
|
| DELETE | /memories |
delete_all_memories |
Requires an id |
| POST | /reset |
reset_memory |
Wipes everything |
| GET | / |
home |
Redirects to /docs |
Auth & admin routers (server/routers/):
| Method | Path | Purpose |
|---|---|---|
| GET | /setup-status |
Whether an admin exists |
| POST | /register |
Create admin/user |
| POST | /login |
Obtain JWT pair |
| POST | /refresh |
Rotate refresh token |
| GET/PATCH | /me |
Current user / update |
| POST | /change-password |
Change password |
| POST | /onboarding-complete |
Mark wizard complete |
| GET/POST/DELETE | /api-keys, /api-keys/{id} |
List / create / revoke per-user API keys |
| GET/DELETE | /entities, /entities/{type}/{id} |
List / delete memory entities |
| GET | /requests |
Request log |
8.4 Deploy flow
- Provision Postgres with the
pgvectorextension. Runserver/init-db.shto create themem0_appdatabase (memories use the defaultpostgresDB). - Set secrets in
.env:JWT_SECRET,OPENAI_API_KEY, andPOSTGRES_*. - Build/run the API container (uvicorn serving
server/main.py) and theserver/dashboardNext.js app. - Run Alembic migrations (
server/alembic/) to create the app tables. - Either register an admin at
http://<host>:3000/setup, or setADMIN_API_KEYfor header-based access.
Warning
The server enables auth by default. If you upgrade an older deployment that had no auth, every protected endpoint returns 401 until you either (1) set
ADMIN_API_KEY, (2) register an admin via/setup, or (3) setAUTH_DISABLED=true(dev only). The server logs a prominent warning on startup when it detects this unconfigured state.
Caution
The prebuilt server image only bundles OpenAI, Anthropic, Gemini (LLM) and OpenAI, Gemini (embedder). Configuring any other provider via
/configurereturns HTTP 400. To add one, install its Python package, extendBUNDLED_LLM_PROVIDERS/BUNDLED_EMBEDDER_PROVIDERSinserver/main.py, and rebuild the image.
9. Deploying OpenMemory
OpenMemory is the turnkey personal deployment. The installer (openmemory/run.sh):
curl -sL https://raw.githubusercontent.com/mem0ai/mem0/main/openmemory/run.sh \
| OPENAI_API_KEY=sk-... VECTOR_STORE=qdrant bash
What it does:
- Validates Docker + Docker Compose V2.
- Generates a
docker-compose.ymlfromcompose/<vector_store>.ymlmerged with theopenmemory-mcpservice. - Selects a vector store (
VECTOR_STOREenv or--vector-storeflag):qdrant(default),chroma,pgvector,redis,weaviate,milvus,elasticsearch,faiss. - Installs the matching vector-store client into the running container and seeds the backend config via
PUT /api/v1/config/mem0/vector_store. - Starts the backend on
:8765and the UI container (mem0/openmemory-ui:latest) on the first free port 3000–3010.
Images used: mem0/openmemory-mcp:latest (backend) and mem0/openmemory-ui:latest (frontend).
MCP integration
The backend exposes an SSE MCP transport:
| Method | Path | Purpose |
|---|---|---|
| GET | /{client_name}/sse/{user_id} |
Open an MCP SSE channel |
| POST | /{client_name}/sse/{user_id}/messages/ |
MCP message channel |
Register that endpoint in any MCP-aware client to give it persistent memory backed by your local store.
Note
OpenMemory stores everything locally (vector store + a SQLite app DB at
openmemory.db). The only outbound dependency is the LLM/embedding provider key you supply.
10. Security
10.1 REST server authentication
The server (server/auth.py) supports three mechanisms, checked in order by verify_auth:
- Bearer JWT — short-lived access tokens (30 min) + rotating refresh tokens (30 days). Refresh tokens are single-use: their
jtiis recorded and atomically consumed via a conditionalUPDATE, closing the replay race. - Per-user API keys — format
m0sk_<urlsafe>; only a 12-char prefix and a bcrypt hash are stored. Lookups match by prefix then bcrypt-verify; keys can be revoked (revoked_at). - Legacy
ADMIN_API_KEY— compared withsecrets.compare_digest(constant-time); maps to the default admin user.
AUTH_DISABLED=true bypasses all of this for local development and logs a warning.
10.2 Hardening details (from source)
- Password hashing: bcrypt via passlib. Login burns equivalent bcrypt cycles for unknown emails (
dummy_verify) to avoid timing-based user enumeration. - JWT: HS256 signed with
JWT_SECRET; server refuses to start (in auth mode) ifJWT_SECRETis unset. - Rate limiting:
slowapiper-IP limiter on auth endpoints. - CORS: restricted to
DASHBOARD_URL. - Config redaction:
/configureredactsapi_key,password,secret,token,jwt_secret, etc. before returning. - Upstream error masking: provider/database/vector errors are classified into safe, generic codes (
provider_auth_failed,provider_rate_limited,vector_store_unavailable, …) with a request ID — internal details aren't leaked to callers. - Request IDs: every request gets an 8-char ID, attached to logs and the
X-Request-IDresponse header.
10.3 Library-side secret handling
The engine's config-cloning logic (_safe_deepcopy_config) redacts sensitive fields and preserves runtime auth objects. Secret detection is layered: an allowlist of runtime fields, an exact deny list, and a suffix deny list (_password, _secret, _token, _credential(s)).
10.4 Platform client auth
MemoryClient sends Authorization: Token <api_key> and a derived Mem0-User-ID (MD5 of the key) header to api.mem0.ai, validating the key against /v1/ping/ on init. Keys are read from MEM0_API_KEY if not passed explicitly.
10.5 Recommendations
- Generate
JWT_SECRETwithopenssl rand -base64 48; rotate on suspected compromise (invalidates all sessions). - Use per-user API keys over the legacy
ADMIN_API_KEYin multi-user deployments; revoke rather than rotate. - Keep provider keys in a secret manager; never commit them. Note the GitHub-issue-tracked CVEs in transitive JS deps — keep the SDKs patched.
- Terminate TLS in front of the server (it speaks plain HTTP); restrict network access to Postgres and the vector store.
11. Operations
11.1 Lifecycle commands (library)
m = Memory.from_config(config)
m.add(...) ; m.search(...) # normal operation
m.close() # release SQLite connections cleanly
m.reset() # DROP history + recreate vector collection (destructive)
11.2 Backup & restore
- Memories: back up your vector store using its native tooling (Qdrant snapshots,
pg_dumpfor pgvector, etc.). This is the source of truth for stored memories. - History: back up the SQLite file at
history_db_path(~/.mem0/history.db, or/app/history/history.dbin the server image). - App data (server):
pg_dumpthemem0_appdatabase (users, API keys, request logs, config overrides).
11.3 Config changes at runtime (server)
POST /configure merges and persists overrides into the settings table (config_overrides key) and rebuilds the shared Memory instance atomically under a lock. Overrides survive restarts (re-merged over DEFAULT_CONFIG on boot).
11.4 Resetting / migrating
- To change vector store or embedder dimensions, you generally must re-index: existing vectors aren't portable across stores or embedding models. Plan a migration that re-
adds source data, or export/import via the Platform's export API. - The
versionfield (v1.1) governs result shaping; keep it stable across a deployment.
11.5 Cost control
Every add() with infer=True is at least one LLM call (plus embeddings); every search() is at least one embedding call. To control spend:
- Use a small, cheap extraction model (the server defaults to
gpt-4.1-nano). - Batch conversation turns before calling
add()rather than per-message. - Use
infer=Falsefor facts you already have structured. - Set
custom_instructionsto narrow extraction and avoid storing noise.
12. Observability & Telemetry
12.1 Anonymous telemetry
Both the library and the server send anonymous telemetry to PostHog by default (opt out with MEM0_TELEMETRY=false).
- Library: captures events like
mem0.init,mem0.add,mem0.searchwith non-identifying properties; entity IDs are processed throughprocess_telemetry_filters(keys + encoded IDs, not raw values). - Server: sends at most two events per install —
admin_registeredandonboarding_completed— carrying only email domain, server version, and a random install UUID. State is tracked in a local JSON file so events fire once.
Important
For privacy-sensitive or regulated deployments, set
MEM0_TELEMETRY=falseacross all processes (library, server, OpenMemory). The flag is read at import/boot time.
12.2 Server logging
- Structured logging with per-request IDs (
%(request_id)sin the log format). - A
request_logstable records method, path, status, latency, and auth type for every non-health, non-docs request (written off the hot path via an executor). - Provider failures log full stack traces server-side while returning sanitized error codes to clients.
12.3 Recommended monitoring
- Vector-store health/latency and disk usage (memory growth is unbounded without lifecycle policies).
- LLM/embedding provider error and rate-limit rates (surfaced as
provider_*error codes). - Postgres connection pool health (
pool_pre_ping=Trueis enabled). request_logslatency percentiles per endpoint.
13. Performance & Scaling
13.1 Built-in optimizations
- Batched everything in
add(): batch embedding, batch vector insert, batch history insert, and batch entity linking — with per-item fallbacks if a batch op fails. - Over-fetch + rerank locally: search over-fetches
max(top_k*4, 60)candidates so the hybrid scorer has a meaningful pool. - Shared spaCy models: entity extraction and lemmatization share a single loaded spaCy instance behind a lock; the lemma model loads with
ner/parserdisabled for speed. - Async twins:
AsyncMemory/AsyncMemoryClientoffload blocking calls viaasyncio.to_thread, enabling concurrency in async servers. - Shared Qdrant client for entity store in embedded mode to avoid RocksDB lock contention.
13.2 Scaling the self-hosted server
- The server holds one shared
Memoryinstance behind a reentrant lock. Heavy concurrent write load serializes on config/instance access — scale horizontally (multiple API replicas) behind a load balancer, all pointing at shared Postgres/pgvector. - pgvector tuning (HNSW vs IVF,
lists, index maintenance) directly affects search latency at scale; size the Postgres instance for your vector count. - Memory growth is unbounded by default. Implement retention (TTL via the Platform's
expires, or periodicdelete_all/pruning of stale scopes) for long-lived deployments.
13.3 Throughput notes
add()latency is dominated by the LLM extraction call. Use a fast model and batch turns.search()latency is embedding + vector search + (optional) reranking. Reranking adds a model call — enable it only when precision matters.
14. Module Reference
mem0/memory/main.py
The engine. Memory and AsyncMemory (sync/async twins of the same logic). Houses the V3 add pipeline, hybrid search, entity store management, and the MemoryBase ABC (get/get_all/update/delete/history).
mem0/client/main.py
MemoryClient / AsyncMemoryClient — the Platform HTTP client. Wraps api.mem0.ai endpoints (/v3/memories/*, /v1/memories/*, /v1/entities, /v1/exports, /v1/feedback, webhooks, projects). Every method is decorated with @api_error_handler which maps HTTP status codes to structured exceptions.
mem0/configs/
Pydantic models. base.py (MemoryConfig, MemoryItem, AzureConfig), enums.py (MemoryType: semantic/episodic/procedural), prompts.py (extraction prompts — the engine's IP), plus per-provider config classes under configs/llms, configs/embeddings, configs/vector_stores, configs/rerankers.
mem0/utils/factory.py
The plug-in registry: LlmFactory, EmbedderFactory, VectorStoreFactory, RerankerFactory. Each maps a provider string to an implementation class (lazy-imported). register_provider() allows runtime extension.
mem0/utils/entity_extraction.py, scoring.py, lemmatization.py, spacy_models.py
Hybrid-search internals: spaCy-based entity extraction (proper nouns, quoted text, noun compounds), BM25 sigmoid normalization, additive scoring (score_and_rank), and the shared spaCy model loader.
mem0/exceptions.py
Structured exception hierarchy rooted at MemoryError (with error_code, details, suggestion, debug_info): AuthenticationError, RateLimitError, ValidationError, MemoryNotFoundError, NetworkError, ConfigurationError, MemoryQuotaExceededError, plus OSS-specific VectorStoreError, EmbeddingError, LLMError, DatabaseError, DependencyError. create_exception_from_response() maps HTTP status → exception.
mem0/proxy/main.py
Mem0 — an OpenAI-compatible chat proxy. .chat.completions.create(...) searches memory, injects it into the prompt, calls the model via LiteLLM, and stores the turn in a background thread.
server/
The FastAPI service: main.py (app + memory endpoints), auth.py (JWT/API-key/admin auth), db.py + models.py (SQLAlchemy: User, APIKey, RequestLog, RefreshTokenJti, Settings), server_state.py (shared Memory instance + config persistence), errors.py (upstream error classification), telemetry.py, rate_limit.py, and routers.
openmemory/
Self-hosted MCP server (api/, with mcp_server.py exposing SSE endpoints) + Next.js UI (ui/) + Docker compose templates (compose/*.yml) + installer (run.sh).
15. Extending Mem0
15.1 Register a custom provider
Factories accept runtime registration:
from mem0.utils.factory import LlmFactory
LlmFactory.register_provider(
name="my_llm",
class_path="my_package.my_llm.MyCustomLLM",
config_class=None, # defaults to BaseLlmConfig
)
Implement the provider class to match the existing interface (e.g. an LLM exposes generate_response(messages, response_format=...); an embedder exposes embed(text, memory_action) and embed_batch(texts, memory_action); a vector store exposes insert/search/list/get/update/delete/keyword_search/reset).
15.2 Custom extraction behavior
- Pass
custom_instructionsinMemoryConfig(orprompt=peradd()) to steer what's extracted without touching code. - For deep changes, the prompts live in
mem0/configs/prompts.py(ADDITIVE_EXTRACTION_PROMPT,generate_additive_extraction_prompt).
15.3 Procedural memory for agents
m.add(messages, agent_id="my-agent", memory_type="procedural_memory")
Produces a single, verbatim-preserving summary of an agent's execution history (driven by PROCEDURAL_MEMORY_SYSTEM_PROMPT). Only "procedural_memory" is accepted as a non-default memory_type.
15.4 Self-host server: add a bundled provider
Install the provider's package into the image, then extend BUNDLED_LLM_PROVIDERS / BUNDLED_EMBEDDER_PROVIDERS in server/main.py and rebuild — otherwise /configure rejects it with HTTP 400.
16. Testing & Quality
The Python suite under tests/ mirrors the package layout: tests/llms, tests/embeddings, tests/vector_stores, tests/rerankers, tests/memory, tests/utils, tests/configs. Vector-store tests are gated on external services via env vars (e.g. ES_URL, WEAVIATE_CLUSTER_URL, RUN_TEST_NEPTUNE_ANALYTICS) so they can run against real backends in CI.
TypeScript packages use Jest/Vitest (mem0-ts, cli/node, vercel-ai-sdk, openclaw), with separate unit/integration/edge/node configs. Run JS tests with each package's test script; Python tests with pytest.
Tip
When validating a provider change, target the matching test directory (e.g.
pytest tests/vector_stores/test_qdrant.py) and set the required env vars for that backend. Many vector-store tests are skipped silently if their service env vars are unset.
17. Known Issues & Improvement Opportunities
Surfaced from the issue tracker and source review:
- Security CVEs in transitive JS deps (closed, fix-pinned via pnpm overrides):
langsmith,@langchain/core,nanoid,axios,immutable,follow-redirects. The TS packages pin overrides — keep them current and re-audit on dependency bumps. - Silent memory loss on partial batch-embedding failure (open, #5245): if batch embedding partially fails in the V3 add pipeline, some extracted facts may be dropped. The code falls back to per-item embedding, but monitor add results when embedding providers are flaky.
- Platform v3
add()and entity IDs (open, #5224): a reported case whereuser_id/agent_idare accepted butget_allreturns empty — validate scope round-trips after upgrades. - Memory poisoning / security scanning (feature requests, #5331/#5349): no built-in defense against adversarial memory injection. If you accept untrusted conversation, consider sanitizing inputs and reviewing
custom_instructions. - Unbounded memory growth: no default retention. Implement TTL/expiry (Platform
expires) or scheduled pruning. - Pluggable OSS storage (proposal, #5354): backend storage is provider-bound; cloud-native pluggability is requested but not yet first-class.
deploy/image provider bundling: the server's bundled-provider allowlist is a deliberate guardrail but a friction point — document clearly which image supports which providers for your team.
18. Troubleshooting (Operator)
Server returns 401 on every endpoint after upgrade
Auth is on by default with no admin configured. Set ADMIN_API_KEY, register an admin at /setup, or set AUTH_DISABLED=true (dev). Watch for the startup warning.
Server won't start: "JWT_SECRET is required"
Set JWT_SECRET (openssl rand -base64 48) or AUTH_DISABLED=true for local dev only.
/configure rejects a provider (HTTP 400)
The image only bundles OpenAI/Anthropic/Gemini (LLM) and OpenAI/Gemini (embedder). Install the package, extend the BUNDLED_* lists in server/main.py, rebuild.
vector_store_unavailable / datastore_unavailable errors
The classifier mapped a Qdrant/pgvector/Postgres failure. Check connectivity, credentials, and that pgvector is installed in the target Postgres DB. Use the X-Request-ID to correlate with server logs.
Memories added but search returns nothing (server)
Confirm scope: /search should pass IDs in filters. Check the embedder/LLM keys are valid (a failed extraction returns empty results). Lower threshold.
High LLM cost
Lower the extraction model tier (MEM0_DEFAULT_LLM_MODEL), batch turns before add(), set custom_instructions to reduce noise, and use infer=False for pre-structured data.
spaCy errors / weak OSS search quality
Install mem0ai[nlp] and en_core_web_sm. Without spaCy, keyword + entity signals are disabled and search falls back to semantic-only.
Telemetry must be disabled for compliance
Set MEM0_TELEMETRY=false in every process (library, server, OpenMemory). It's read at import/boot.
19. Quick Reference
Service endpoints (self-hosted server)
POST /memories GET /memories GET /memories/{id}
POST /search PUT /memories/{id} GET /memories/{id}/history
DELETE /memories/{id} DELETE /memories POST /reset
GET/POST /configure GET /configure/providers POST /generate-instructions
POST /register /login /refresh GET/PATCH /me POST /change-password
GET/POST/DELETE /api-keys GET/DELETE /entities GET /requests
GET / (→ /docs Swagger UI)
Default server config values
| Setting | Default |
|---|---|
| Vector store | pgvector (postgres DB, memories collection) |
| LLM | OpenAI gpt-4.1-nano-2025-04-14, temp 0.2 |
| Embedder | OpenAI text-embedding-3-small |
| History DB | /app/history/history.db |
| Access token TTL | 30 min |
| Refresh token TTL | 30 days |
| App DB | mem0_app |
Critical environment variables
| Variable | Required? | Notes |
|---|---|---|
JWT_SECRET |
Server (unless AUTH_DISABLED) |
JWT signing |
OPENAI_API_KEY |
Server default / OSS default | LLM + embedder |
POSTGRES_* |
Server | pgvector + app DB |
MEM0_API_KEY |
Platform client / CLI | api.mem0.ai auth |
MEM0_DIR |
OSS | History dir (~/.mem0) |
MEM0_TELEMETRY |
All | false to opt out |
ADMIN_API_KEY / AUTH_DISABLED |
Server | Auth shortcuts |
Factories (extension points)
| Factory | Selects | Register method |
|---|---|---|
LlmFactory |
LLM provider | register_provider(name, class_path, config_class) |
EmbedderFactory |
Embedder | (edit provider_to_class) |
VectorStoreFactory |
Vector store | (edit provider_to_class) |
RerankerFactory |
Reranker | (edit provider_to_class) |
Exception hierarchy (library)
MemoryError → AuthenticationError, RateLimitError, ValidationError, MemoryNotFoundError, NetworkError, ConfigurationError, MemoryQuotaExceededError, MemoryCorruptionError, VectorSearchError, CacheError, VectorStoreError, EmbeddingError, LLMError, DatabaseError, DependencyError.