You built a CrewAI crew, flipped on memory=True, and it worked — the agents remembered things across tasks. Then you tried to ship it, and a question landed: where is that memory actually living? The answer, until recently, was "a default vector store CrewAI stood up for you, in a location and shape you don't control." In production, that's not a convenience. It's a dependency you can't tune, can't audit, and can't point at the infrastructure your platform team already runs.
CrewAI 1.14 fixed the posture: memory, knowledge, RAG, and flow are now pluggable backends. This is the hands-on version of that change — how to move CrewAI's memory onto a Qdrant instance you own. There are two paths. Pick by how much Mem0 you want in your stack.
Path 1: ExternalMemory + Mem0 (config-only, fastest)#
The framework-native route uses CrewAI's ExternalMemory with the Mem0 provider, and Mem0's bring-your-own-store support to write into your Qdrant. No new classes — just a config dict.
from crewai import Crew, Agent, Task
from crewai.memory.external.external_memory import ExternalMemory
external_memory = ExternalMemory(
embedder_config={
"provider": "mem0",
"config": {
"user_id": "crew-prod-1",
"infer": True,
"local_mem0_config": {
"vector_store": {
"provider": "qdrant",
"config": {"host": "localhost", "port": 6333},
},
"llm": {
"provider": "openai",
"config": {"model": "gpt-4o-mini"},
},
"embedder": {
"provider": "openai",
"config": {"model": "text-embedding-3-small"},
},
},
},
}
)
crew = Crew(
agents=[...],
tasks=[...],
external_memory=external_memory,
)
Here is the field name that will trip you: embedder_config. Despite the name, it does not carry only the embedding model — it carries the entire external-memory configuration: the Mem0 provider, the Qdrant vector store, the LLM Mem0 uses to extract memories, and the embedder. Read it as "external-memory config" and the shape stops looking strange.
Pointvector_storeat a managed Qdrant Cloud cluster instead oflocalhostand the same fifteen lines become a production deployment — the code doesn't change, only the host.
What this actually swaps (and what it doesn't)#
This is the detail most tutorials skip, and it matters for correctness. Plugging in ExternalMemory via Mem0 takes over short-term and entity memory — those move into your Qdrant. But CrewAI's long-term memory (the SQLite-backed record of task outcomes) and contextual memory stay handled natively.
So you don't get one clean store with everything in it. You get a hybrid: Qdrant for the recall-heavy short-term and entity layers, local SQLite for durable task history. If your compliance requirement is "all embeddings live in our Qdrant," that's satisfied. If it's "no state touches local disk at all," you have a second thing to relocate — plan for it now rather than discovering the .db file in an audit later. (If you're weighing Mem0 against alternatives for that memory layer, we compared Mem0 vs Zep vs Letta and shipped a Mem0 quickstart that goes deeper on the extraction side.)
Path 2: A custom QdrantStorage class (no Mem0)#
If you don't want Mem0 in the loop — you'd rather own the storage code and depend only on qdrant-client — CrewAI lets you attach a custom storage object directly to each memory type. Qdrant's own docs publish a QdrantStorage example that extends CrewAI's RAGStorage base class:
from crewai import Crew
from crewai.memory.short_term.short_term_memory import ShortTermMemory
from crewai.memory.entity.entity_memory import EntityMemory
from mycrew.storage import QdrantStorage # your class, extends RAGStorage
crew = Crew(
agents=[...],
tasks=[...],
memory=True,
short_term_memory=ShortTermMemory(storage=QdrantStorage("short-term")),
entity_memory=EntityMemory(storage=QdrantStorage("entity")),
)
One honest caveat: QdrantStorage is not part of CrewAI. It's an example implementation you add to your project (here, mycrew.storage), subclassing RAGStorage to handle Qdrant embedding writes and reads. That's more work than Path 1, but it buys you a storage layer with no Mem0 dependency and no extraction LLM you didn't ask for — just your embeddings, your collections, your client. For teams that already run Qdrant and want CrewAI to be only orchestration, this is the cleaner seam.
Which one to reach for#
- Want it working today, and Mem0's automatic memory extraction sounds useful? Path 1. It's config, not code, and it's the route CrewAI 1.14 built for exactly this.
- Want zero Mem0, full control of the storage code, and only a
qdrant-clientdependency? Path 2. Write theRAGStoragesubclass once and CrewAI treats it like any other backend.
Either way, the point of 1.14 stands: the vector store your agents remember into is now yours to run, tune, and place. The whole framework category is converging on this — orchestration on top, swappable infrastructure underneath. Owning the memory backend is no longer an advanced move. As of 1.14, it's a config field.



