How do you stop CrewAI memory leaking between users in a multi-tenant service?
answer
- scoped to a location, not a person
- retrieval has no tenant predicate
- one root, one writer
- the boring option removes the problem
- deletion now reaches embeddings
basics
~20 sCrewAI's built-in memory is scoped to a storage location, not to a user, so one shared directory means every tenant's content is retrievable by every run. Isolate by giving each tenant its own storage root and process, delegate to a user-scoped external memory provider, or run with memory disabled.
solid answer
~40 sThe hazard is structural: `Crew(memory=True)` persists short-term, entity and long-term memory under the CrewAI storage directory, and retrieval matches on semantic similarity with no tenant predicate. Two customers served by one process sharing one directory can retrieve each other's material. There are three defensible designs. **Isolate by location** — set `CREWAI_STORAGE_DIR` per tenant and keep one tenant per process, since a filesystem-backed SQLite plus vector store is not something to have concurrent writers contend over. **Delegate memory** — configure an external provider through `memory_config` with a `user_id`, moving scoping into a system that models users natively. **Or run stateless** — leave `memory=True` off, pass per-request context into task descriptions, and reserve knowledge sources for genuinely shared corpora. Whichever you pick, add a reset boundary and treat the storage directory as sensitive data at rest.
go deeper
Understand that crew memory is tied to a storage location rather than to a person, so nothing automatically keeps one user's data out of another user's run.
Explain that retrieval matches on similarity with no tenant filter, and name the concrete levers: a per-tenant CREWAI_STORAGE_DIR, an external memory provider carrying a user id, or leaving memory off entirely.
Weigh the options against real constraints — one writer per filesystem store, cold-start and storage cost of per-tenant directories, added latency and a vendor dependency for external memory — and specify the reset boundary you would enforce.
Own the whole consequence chain: isolation architecture, deletion and retention obligations that now reach derived embeddings, encryption of the store, and the explainability gap created by context that is injected automatically and never logged.
## Why this is a design question, not a config question CrewAI's memory model was built for a crew that belongs to you: it accumulates what the crew has seen and retrieves it by similarity. There is no tenant dimension in that model. Enabling `memory=True` in a service that handles many customers therefore converts a stateless request handler into a shared, cross-customer knowledge store — and the retrieval path has no filter that would keep customer A's contract terms out of the context assembled for customer B. Every part of the stack behaves exactly as documented; the leak comes from applying a single-owner abstraction to a multi-owner problem. ## The three defensible architectures **1. Isolate by storage location and process.** Set `CREWAI_STORAGE_DIR` to a per-tenant path and run one tenant per process (or one per worker, dispatched by tenant). This maps the isolation boundary onto something the framework actually respects: the store it reads and writes. The constraints are real — long-term memory is SQLite and the vector collections are file-backed, so multiple concurrent writers on one root is a correctness and locking hazard, and per-tenant directories multiply storage and cold-start costs. It suits a small number of high-value tenants far better than a long tail of small ones. **2. Delegate memory to a user-scoped provider.** `memory_config` lets you point memory at an external provider (Mem0, for example) carrying a `user_id`. Scoping then lives in a system whose data model has users in it, and your crew processes can stay stateless and horizontally scalable. You trade a local file for a network dependency in the hot path, another vendor holding customer data, and latency on every read and write. For a service with many users this is usually the honest answer. **3. Run stateless and pass context explicitly.** Leave memory off. Fetch whatever history matters from your own datastore — where it is already governed, access-controlled and auditable — and inject it into the task description or as a per-request string knowledge source. This is the most boring option and frequently the right one: your application already knows who the user is and what they are allowed to see, while CrewAI's memory does not and never will. You give up automatic accumulation and gain reproducibility, testability and a clean deletion story. ## Where knowledge sources fit Knowledge is a different risk profile. A shared corpus — product docs, policies, public reference material — is fine to index once and serve to everyone, because it is authored and identical for all tenants. Tenant-specific documents are not: indexing customer A's uploads into a crew-level knowledge base makes them retrievable by anyone the crew serves. Agent-level `knowledge_sources` scope material to an agent, which is a role boundary, not a customer boundary — do not mistake one for the other. Tenant-specific documents belong in a per-tenant store with a per-tenant lifecycle. ## Obligations you inherit the moment memory is on - **Deletion.** "Delete my data" now has to reach embeddings derived from that data. `crew.reset_memories(...)` and `crewai reset-memories` are your instruments, and they are coarse — per-user deletion is only tractable if the user maps to a whole store or to an external provider that supports it. - **Retention.** Nothing prunes memory. Left alone, it grows unboundedly and keeps content long past any policy you have written down. - **Data at rest.** The storage directory holds embeddings and task records derived from customer inputs. It inherits their classification: encrypt the volume, keep it off shared machines, and include it in backup and access reviews. - **Auditability.** Memory is injected into prompts automatically, so "why did the model say that" may have an answer that lives in a vector store nobody logged. If you need explainability, capture what was retrieved. ## Choosing A useful decision order: does this crew genuinely benefit from cross-run accumulation? If not, run stateless — you remove the entire class of problem. If yes, how many tenants? A handful of large ones justifies per-tenant storage and processes. A long tail argues for an external user-scoped provider. And whichever you choose, write down the reset boundary — per session, per tenant offboarding, per retention window — because unbounded accumulation with no defined reset is how a convenience flag becomes a compliance finding. ## Interview framing Lead by naming the structural fact — memory is scoped to a storage location and retrieval has no tenant predicate — rather than reaching for a fix. Then give the three architectures with their honest tradeoffs, separate the knowledge risk from the memory risk, and finish on deletion, retention and encryption obligations. That is a principal-level answer; "use a different collection per user" alone is not.
- Does scoping knowledge sources to a single agent give you tenant isolation?No. Agent-level knowledge_sources draw a role boundary — this agent sees this corpus — not a customer boundary. Every request the crew serves routes through the same agents, so a tenant-specific document indexed there is still reachable by any user whose request that agent handles. Tenant documents need a per-tenant store and lifecycle, not a per-agent attachment.
- Why not simply run many tenants through one process with per-tenant storage directories?Because the storage root is read from configuration for the process, and swapping it per request invites races while concurrent writers contend over a SQLite file and file-backed vector collections. Per-tenant directories work when the process is also per-tenant. If you need many tenants per process, move scoping into an external provider that models users rather than paths.
- What does a right-to-delete request cost you once memory is enabled?You must reach embeddings and task records derived from that user's content, not just rows in your own database. CrewAI's reset instruments are coarse — they clear stores, not individual users — so per-user deletion is only practical if a user maps onto a whole storage location or onto an external provider that supports user-scoped deletion. Design that before enabling memory, not after.
- When is the stateless design actually better rather than a cop-out?Whenever your application already holds the history and the authorization rules for it. Fetching the relevant context yourself and injecting it per request keeps retrieval governed by your access control, makes runs reproducible and testable, and gives deletion a single clean location. You lose automatic accumulation, which many production crews never needed in the first place.
saying these in an interview costs you the question
- Assumes CrewAI memory is scoped per user by default
- Treats agent-level knowledge as a tenant boundary
- Shares one storage directory across concurrent tenant workers
- Enables memory without a deletion or retention plan
- Ignores that the store holds customer-derived data at rest