Design a Multi-Tenant AI SaaS Platform — the walkthrough in full
A written version of the interactive walkthrough above — the same steps, decisions and trade-offs, laid out for reading, reference and search.
The big idea
Shared infrastructure, zero cross-tenant leakage
Serving many separate customer organizations from one shared platform is efficient — until tenant A's private data ends up retrieved into tenant B's answer, or tenant C's traffic spike makes every other tenant's product feel broken. Multi-tenancy done right is invisible to every tenant; done wrong, it's a data breach or an outage waiting to happen.
We'll build isolation as a first-class, multi-layer property — not application-level trust — plus the operational realities of running one platform for many customers: customization without duplication, noisy-neighbor prevention, cost attribution, and compliance-driven data residency.
How to read this: Each step opens with a real design decision — make the call before I show you what ships. Watch the diagram grow, hover the boxes, and at the end introduce a scoping bug and spike a noisy neighbor to see what defense-in-depth and quotas actually catch. Hit Begin.
Step 1 · The baseline
One shared config, one shared index
Simplest version: every tenant's documents go into one shared vector index, and everyone uses the same prompt and configuration. What's the danger, specifically for a retrieval-augmented system?
Design decision: All tenants' documents share one unscoped vector index. What's the specific danger?
The call: A retrieval query from Tenant B could return and surface Tenant A's private documents in Tenant B's answer — a direct data leak between completely separate customer organizations, some of whom may be direct competitors. — Without tenant scoping enforced on retrieval, similarity search has no concept of "whose data this is" — it just finds the closest-matching vectors regardless of tenant, meaning one tenant's private, confidential documents can genuinely surface in another tenant's query results. For a B2B SaaS product, this is often the single most catastrophic possible failure.
An unscoped shared index risks one tenant's private documents surfacing directly in another tenant's retrieval results — a severe data breach, not a minor quality issue. Tenant isolation has to be enforced at the data layer, from the very first design decision, not treated as an afterthought.
Multi-tenancy's central risk is invisible until it isn't: A shared, unscoped system can appear to work fine in testing (if test data happens not to overlap meaningfully) while carrying a live, catastrophic leakage risk the moment two tenants' real data genuinely differ in ways retrieval can conflate — this is exactly why isolation needs to be a designed guarantee, not an assumption.
Step 2 · Enforce isolation at the data layer
Every query, independently scoped
Fix Step 1: every retrieval and every data access must be scoped to exactly one tenant. Should this scoping be trusted to the application layer alone?
Design decision: Tenant scoping needs to be enforced somewhere. Is trusting the application/API layer alone sufficient?
The call: No — enforce tenant scoping independently at the DATA layer itself (every query to the data store includes and verifies a tenant id, checked by the store, not just trusted from upstream), in addition to the API layer. — Independent, redundant enforcement at the data layer means even if the API layer's own tenant check has a bug, the data store's OWN check still blocks the cross-tenant access — a genuine defense-in-depth property that a single point of enforcement can never provide.
Enforce tenant scoping independently at the data layer, not just trusted from the API — every query to Tenant-Isolated Data carries and verifies a tenant id, checked by the store itself. This gives genuine defense-in-depth: a bug at any single layer doesn't automatically mean a full cross-tenant breach.
Enforce boundaries in the platform, redundantly, not just once: This echoes a theme from agent tool-permission enforcement — a boundary that matters shouldn't depend on a single point of correctness. For tenant isolation specifically, the stakes (a full data breach) justify enforcing the check at more than one independent layer.
Step 3 · Customization without duplicated infrastructure
Different prompts, one shared runtime
Tenants want different system prompts, different knowledge-base scopes, maybe different feature sets. Does each tenant need its own separately-deployed copy of the whole application stack?
Design decision: Tenants want meaningfully different behavior. Does each need a fully separate deployed stack?
The call: No — run one shared runtime, and resolve tenant-specific behavior (prompts, retrieval scope, feature flags) from Per-Tenant Config at request time, so the underlying application code and infrastructure stay shared while behavior genuinely differs per tenant. — A shared runtime with tenant-scoped CONFIGURATION resolved per request gives each tenant meaningfully different behavior — different prompts, different feature flags, different retrieval scope — without needing to deploy or maintain a separate copy of the actual application for each one. Updates and fixes ship once, to everyone, automatically.
Use one shared runtime with Per-Tenant Config resolved at request time — prompts, retrieval scope, and feature flags differ per tenant, while the underlying application code and infrastructure remain shared. Tenant admins configure their own instance through this layer, without needing platform engineering to deploy anything separately.
Separate what varies from what stays shared: The same code-vs-configuration separation used for feature flags and prompt management elsewhere applies here at the tenant level: what SHOULD differ per tenant (prompts, scope) lives in configuration; what SHOULDN'T need to differ (the application logic itself) stays one shared, maintainable codebase.
Step 4 · Prevent noisy neighbors
One tenant's spike shouldn't degrade everyone else
All tenants share the same underlying infrastructure. One tenant suddenly sends 50x their normal traffic. Should that be allowed to affect other tenants' latency or availability?
Design decision: One tenant spikes traffic dramatically on shared infrastructure. Should other tenants feel the effect?
The call: No — enforce per-tenant rate limits and resource quotas, so a spiking tenant is throttled at their own boundary while every other tenant's experience on the shared infrastructure remains unaffected. — Per-tenant quotas contain the blast radius of any single tenant's traffic pattern to that tenant alone — the spiking tenant experiences their own throttling, while the shared infrastructure's capacity for every other tenant stays exactly as available as before.
Add a Quota Enforcer: per-tenant rate limits and resource quotas mean a traffic spike from one tenant is contained to throttling at THEIR boundary, with zero impact on the latency or availability every other tenant experiences on the same shared infrastructure.
Shared infrastructure shouldn't mean shared blast radius: This is the same fair-share/quota principle that shows up across very different domains in this series (dating-app fairness, GPU scheduling, online-judge queueing) — whenever multiple parties share a finite resource, per-party bounds are what keep one party's behavior from degrading everyone else's experience.
Step 5 · Attribute cost per tenant
Know exactly what each tenant actually costs
Tenants have vastly different usage patterns — some send a handful of requests a day, others thousands. Should pricing be flat regardless of actual usage?
Design decision: Tenant usage varies enormously. Should billing be a flat rate regardless of actual consumption?
The call: No — track exact LLM and infrastructure cost per tenant per request, and use that Cost Attribution data as the basis for accurate, usage-based billing. — Precise per-tenant cost tracking means pricing can actually reflect real consumption — light users pay less, heavy users pay proportionally more, and the platform's margins stay predictable regardless of how usage patterns vary across the tenant base.
Track Cost Attribution precisely per tenant, per request — the real LLM and infrastructure cost each tenant actually generates. This becomes the basis for usage-based billing that fairly reflects real consumption, rather than a flat rate that over- or under-charges depending on how each tenant actually uses the product.
Billing accuracy depends on cost visibility: You can't price fairly what you don't measure precisely — per-tenant cost attribution is the prerequisite for any pricing model more sophisticated than an undifferentiated flat rate, and it's also simply useful operationally for knowing which tenants and use cases are actually profitable.
Step 6 · Data residency and compliance
Some tenants' data can't leave a region
An enterprise tenant in the EU requires their data to stay within EU infrastructure for regulatory reasons. Can they be served from the same global deployment as every other tenant?
Add a Region Router that directs a tenant's requests and storage to region-specific infrastructure when their compliance requirements demand it — a tenant with data-residency requirements is transparently routed to and stored in the correct region, while tenants without such requirements continue using the default global deployment. This is a per-tenant configuration choice, not a platform-wide architectural fork.
Compliance requirements are a per-tenant property, not a platform-wide one: Not every tenant needs data residency guarantees, so building the platform to ROUTE tenants that do need it to appropriate infrastructure — rather than either ignoring the requirement entirely or forcing every tenant through the most restrictive regional constraint — serves both populations correctly.
Step 7 · Automated tenant onboarding
A new customer shouldn't need custom engineering
A new enterprise customer signs up. Should provisioning their isolated configuration, storage, and quotas require manual, bespoke engineering work each time?
Make onboarding self-service and automated: creating a new tenant's isolated config entry, data-store scope, and default quotas is a fast, automated provisioning flow the Tenant Admin can trigger themselves — not a manual, per-customer engineering task that doesn't scale as the platform grows its customer base.
Operational scale requires automated provisioning: A platform that requires manual engineering effort to onboard every new tenant caps its own growth at whatever rate engineers can manually provision customers — automating the entire provisioning path is what lets the tenant base grow independent of platform-team headcount.
Step 8 · The sharp edges
Defense in depth, and genuine deletion
Two of the highest-stakes real-world requirements: a bug in any single isolation layer must not cause a full cross-tenant breach, and when a tenant leaves, their data needs to actually, verifiably disappear.
Isolation is enforced independently at multiple layers — API-level tenant scoping AND data-layer tenant verification — so a bug in either one alone doesn't produce a full breach, as demonstrated by the Step 2 chaos scenario. For tenant offboarding, deletion must be genuine and verifiable across every store the tenant's data touched (documents, embeddings, config, logs, backups) — not simply marking the tenant inactive while their data quietly persists, which fails compliance requirements like the GDPR right to erasure.
Design for the unhappy path: A scoping bug at one layer → caught by independent enforcement at another. A noisy neighbor → contained by per-tenant quotas. A departing tenant → genuinely, verifiably deleted, not just deactivated. A multi-tenant platform that only stays safe when every layer works perfectly is a demo; one that survives a bug in any single layer is a product.
You did it
You just designed a multi-tenant AI SaaS platform.
- A — n
- T — e
- P — e
- P — e
- P — r
- A —
- A — u
- T — e