01

Requirements & scope

Problem Statement & Requirements

Design a multi-tenant Software-as-a-Service platform architecture that serves thousands of organizations (tenants) on shared infrastructure while providing data isolation, per-tenant customization, fair resource allocation, and tiered pricing — the foundational pattern behind Slack, Salesforce, Shopify, Datadog, Atlassian, and virtually every modern B2B SaaS product.

Functional Requirements

  • Tenant onboarding — self-service signup with organization creation, admin user, initial configuration
  • Tenant isolation — each tenant's data is logically (or physically) separated; no cross-tenant data leakage
  • Tenant-level configuration — custom branding (logo, colors, domain), feature flags, workflow configuration per tenant
  • User management — tenant admins manage their own users, roles, and permissions (RBAC within tenant)
  • Tiered plans — Free, Pro, Enterprise tiers with different feature sets, limits, and pricing
  • Usage metering & billing — track per-tenant usage (API calls, storage, seats) and bill accordingly
  • Tenant-aware API — all API endpoints are tenant-scoped; tenant resolved from auth token, subdomain, or header
  • Admin console — platform-level admin to manage tenants, view usage, handle support escalations
  • Data export / portability — tenants can export their data (GDPR, vendor lock-in concerns)
  • Custom domains — Enterprise tenants use their own domain (acme.myproduct.com or app.acme.com)

Non-Functional Requirements

  • Noisy neighbor prevention — one tenant's traffic spike must not degrade others
  • Scalability — support 100K+ tenants, from 1-user free tier to 100K-user enterprise
  • Security — tenant data isolation is paramount; penetration of one tenant must not expose another
  • Availability — 99.99% for all tenants; 99.999% SLA for Enterprise tier
  • Cost efficiency — shared infrastructure for small tenants; dedicated resources for large ones
  • Compliance — SOC 2, GDPR, HIPAA (for healthcare SaaS); data residency per region
  • Zero-downtime deployments — rolling updates without interrupting any tenant

Out of Scope

  • Specific SaaS product features (CRM, project management, etc.)
  • Marketplace / app ecosystem (Shopify App Store model)
  • Full identity provider implementation (use Auth0/Okta integration)
02

Scale estimations

Scale Estimations

Traffic

Metric Value
Total tenants 100K
Free tier tenants (1-5 users) 80K (80%)
Pro tier tenants (5-500 users) 18K (18%)
Enterprise tier tenants (500-100K users) 2K (2%)
Total end users 50M
Daily active users 10M
API requests / second (total) 200K RPS
API requests / second (single large tenant peak) 20K RPS
Webhook deliveries / day 100M

Storage

Metric Value
Avg data per Free tenant 100 MB
Avg data per Pro tenant 10 GB
Avg data per Enterprise tenant 500 GB
Total: Free (80K × 100 MB) ~8 TB
Total: Pro (18K × 10 GB) ~180 TB
Total: Enterprise (2K × 500 GB) ~1 PB
Grand total ~1.2 PB
File/blob storage (attachments) ~3 PB
Audit logs (all tenants, 1 year) ~50 TB

Compute

Metric Value
API servers 200-500 (auto-scaling)
Background workers 100-200
Database shards 50-100 (shared pool)
Dedicated DB instances (Enterprise) 50-100
Redis cache nodes 30-50
Tenant isolation overhead ~10-15% of total compute (auth, routing, metering)
03

Layered architecture

High-Level Architecture

High-Level ArchitectureExcalidraw diagram · editable shapes · reveal step by stepExplore

Component Breakdown

Component BreakdownExcalidraw diagram · editable shapes · reveal step by stepExplore
04

API & contracts

API Design

Tenant Onboarding

EXAMPLE
POST /api/v1/tenants
Content-Type: application/json

Request:
{
  "organization_name": "Acme Corp",
  "subdomain": "acme",                  // acme.myproduct.com
  "admin_email": "alice@acme.com",
  "plan": "pro",                         // free | pro | enterprise
  "billing": {
    "payment_method_id": "pm_card_4242"
  }
}

Response (201 Created):
{
  "tenant_id": "tenant-abc123",
  "organization_name": "Acme Corp",
  "subdomain": "acme",
  "url": "https://acme.myproduct.com",
  "plan": "pro",
  "status": "provisioning",             // provisioning → active
  "admin_user": {
    "user_id": "user-alice",
    "email": "alice@acme.com",
    "role": "admin",
    "invite_link": "https://acme.myproduct.com/invite/..."
  },
  "provisioning_eta_seconds": 30,
  "created_at": "2026-04-03T10:00:00Z"
}

// Behind the scenes (provisioning pipeline):
// 1. Create tenant record in control plane DB
// 2. Provision database schema / shard allocation
// 3. Seed default configuration (roles, settings)
// 4. Create admin user + send invite email
// 5. Configure subdomain DNS
// 6. Issue TLS certificate (if custom domain)
// 7. Status → "active"

Tenant-Scoped API Call

EXAMPLE
GET /api/v1/projects
Authorization: Bearer <JWT with tenant_id=acme>
# OR
Host: acme.myproduct.com

Response (200 OK):
{
  "projects": [
    {
      "project_id": "proj-001",
      "name": "Q2 Launch",
      "members": 12,
      "created_at": "2026-03-15T10:00:00Z"
    },
    ...
  ]
}

// This ONLY returns projects belonging to tenant "acme"
// Even if database has 100K tenants' projects,
// query is: SELECT * FROM projects WHERE tenant_id = 'acme'
// RLS policy enforces this even if code has a bug

Usage & Billing

EXAMPLE
GET /api/v1/tenants/{tenant_id}/usage?period=2026-04

Response (200 OK):
{
  "tenant_id": "tenant-abc123",
  "period": "2026-04",
  "plan": "pro",
  "usage": {
    "seats": {"used": 47, "limit": 100, "overage": 0},
    "api_calls": {"used": 2340000, "limit": 5000000},
    "storage_gb": {"used": 8.7, "limit": 50},
    "projects": {"used": 23, "limit": 50}
  },
  "billing": {
    "base_charge": 9900,              // $99/month base
    "seat_charge": 4700,              // 47 seats × $1/seat
    "overage_charges": 0,
    "total": 14600,                   // $146.00
    "currency": "usd"
  }
}

Platform Admin

EXAMPLE
GET /api/v1/admin/tenants?plan=enterprise&sort=storage_desc&limit=20
Authorization: Bearer <platform_admin_token>

Response (200 OK):
{
  "tenants": [
    {
      "tenant_id": "tenant-bigcorp",
      "name": "BigCorp Inc.",
      "plan": "enterprise",
      "users": 45000,
      "storage_tb": 1.2,
      "api_calls_per_day": 5000000,
      "monthly_revenue": 250000,
      "health": "healthy",
      "shard": "dedicated-bigcorp-01"
    },
    ...
  ]
}
05

Data model

Data Model

Tenant (Control Plane)

Tenant (Control Plane)Excalidraw diagram · editable shapes · reveal step by stepExplore

Tenant-Scoped Data (Application Tables)

Tenant-Scoped Data (Application Tables)Excalidraw diagram · editable shapes · reveal step by stepExplore

Usage Metering

Usage MeteringExcalidraw diagram · editable shapes · reveal step by stepExplore
06

Core design decisions

Core Design Decisions

Decision 1: Data Isolation Model — The Most Critical Choice

Decision 1: Data Isolation Model — The Most Critical ChoiceExcalidraw diagram · editable shapes · reveal step by stepExplore

Decision 2: Noisy Neighbor Prevention

Decision 2: Noisy Neighbor PreventionExcalidraw diagram · editable shapes · reveal step by stepExplore

Decision 3: Tenant-Aware Caching

Decision 3: Tenant-Aware CachingExcalidraw diagram · editable shapes · reveal step by stepExplore
07

Request flows

Detailed Flow Diagrams

Request Flow (Tenant Resolution → Execution → Response)

Request Flow (Tenant Resolution → Execution → Response)Excalidraw diagram · editable shapes · reveal step by stepExplore

Tenant Provisioning Flow

Tenant Provisioning FlowExcalidraw diagram · editable shapes · reveal step by stepExplore

Tenant Data Migration (Upgrade to Enterprise)

Tenant Data Migration (Upgrade to Enterprise)Excalidraw diagram · editable shapes · reveal step by stepExplore
08

Performance & caching

Workshop note · added for the website’s common reading format

Start with the dominant access pattern of Multi-Tenant SaaS Platform. Shared database is one place to inspect capacity and tail latency. Measure before introducing a cache: define the cache key, invalidation policy, stale-data budget, and cold-start behavior.

Batch independent work where the latency budget allows it. Bound queues and concurrency, and verify that an optimization does not move the bottleneck to a dependency.

09

Advanced design

Feature Flags & Plan-Based Access

Feature Flags & Plan-Based AccessExcalidraw diagram · editable shapes · reveal step by stepExplore

Billing & Usage Metering

Billing & Usage MeteringExcalidraw diagram · editable shapes · reveal step by stepExplore

Security & Compliance

Security & ComplianceExcalidraw diagram · editable shapes · reveal step by stepExplore
10

Edge cases

Handling Edge Cases

Tenant Deletion (GDPR Right to Erasure)

EXAMPLE
Tenant requests account deletion:

  1. Immediate: Mark tenant as "pending_deletion" (30-day grace)
     → Tenant can still cancel deletion within 30 days
     → All access blocked after request

  2. Day 30: Hard deletion pipeline
     → Shared DB: DELETE FROM all_tables WHERE tenant_id = ?
       (batch delete, 10K rows at a time to avoid lock contention)
     → Dedicated DB: DROP DATABASE tenant_acme
     → Blob storage: DELETE s3://bucket/tenants/acme/*
     → Cache: SCAN + DEL all keys matching acme:*
     → Search index: delete all documents for tenant
     → Audit logs: retained (anonymized) for compliance
     → Backups: tenant data excluded from future backups;
       old backups expire naturally (30-90 day retention)

  3. Confirmation: generate deletion certificate
     → Cryptographic proof that all data was purged
     → Required by GDPR Article 17

Tenant Suspended (Non-Payment)

EXAMPLE
Billing failure for tenant "acme":

  Day 0: Payment fails → retry
  Day 3: Second attempt fails → email warning
  Day 7: Third attempt fails → mark tenant as "payment_overdue"
         → Banner in UI: "Update payment method to continue"
         → All features still accessible (grace period)
  Day 14: Mark as "suspended"
         → Read-only access (can view data, cannot modify)
         → API writes return 402 Payment Required
         → Background jobs paused
  Day 30: Mark as "pending_deletion"
         → Data export available (30 more days)
  Day 60: Hard deletion (same as GDPR flow)

  At any point: payment succeeds → immediately reactivate
  → Status returns to "active"
  → Zero data loss

Cross-Tenant Data Leak Prevention

EXAMPLE
Defense in depth (5 layers):

  Layer 1: Tenant Context Middleware
    → Every request resolves tenant_id
    → Attached to request context
    → Service layer reads from context

  Layer 2: Row-Level Security (PostgreSQL)
    → Database enforces tenant_id filter
    → Even buggy application code can't cross tenants
    → Policy: USING (tenant_id = current_setting('app.tenant'))

  Layer 3: Cache Key Namespace
    → All cache keys prefixed with tenant_id
    → Framework enforces: if key doesn't start with tenant_id, reject

  Layer 4: API Response Validation
    → Response interceptor checks: do ALL returned objects
      belong to the request's tenant_id?
    → If mismatch detected → 500 error + security alert + block response

  Layer 5: Automated Testing
    → Integration tests with 2 test tenants
    → Verify: actions on Tenant A never return Tenant B's data
    → Run on every deployment (CI/CD gate)
    → Penetration testing quarterly

Schema Migrations Across Tenants

EXAMPLE
Problem: 100K tenants on shared DB → ALTER TABLE affects everyone

Shared-table model (recommended migrations):
  1. Additive changes only: ADD COLUMN (nullable), ADD INDEX
     → Online DDL in PostgreSQL/MySQL (no locks for most operations)
     → All tenants get the new column simultaneously
     → Application code handles both old (null) and new values

  2. Breaking changes (rare):
     → Blue-green migration:
       a. Create new table with new schema
       b. Backfill data from old table (background, batched)
       c. Dual-write: both old and new tables
       d. Switch reads to new table
       e. Stop writing to old table
       f. Drop old table
     → Zero downtime for any tenant

  Dedicated DB model (Enterprise):
  → Migration applied per-instance via rolling update
  → Can canary: migrate 5% of instances → verify → rollout rest
  → Failed migration: rollback that instance only
11

Tradeoffs

Tradeoffs & Design Decisions Summary

Decision Option A Option B Chosen Why
Data isolation All shared (pool) All dedicated (silo) Hybrid: shared for Free/Pro, dedicated for Enterprise Cost-effective for small tenants; full isolation for large/compliance tenants
Tenant resolution JWT claim only Subdomain + JWT + custom domain Multi-method Subdomain for web UX; JWT for API; custom domain for Enterprise branding
Query isolation Application-level WHERE PostgreSQL RLS RLS + application RLS as safety net catches application bugs; defense in depth
Rate limiting Global only Per-tenant Per-tenant + per-plan Prevents noisy neighbor; plan-based limits drive upgrades
Cache isolation Shared with prefix Dedicated per tenant Shared (Free/Pro) + dedicated (Enterprise) Shared is cost-effective; Enterprise gets full isolation
Billing model Flat rate only Usage-based only Hybrid (base + seat + usage overage) Base provides predictable revenue; usage captures growth; overage monetizes heavy users
Feature gating Plan column check Feature flag service Feature flag service + plan mapping Flexible: can enable features per-tenant (beta), per-plan, or globally
Schema migrations Downtime window Online DDL + blue-green Online DDL (additive) + blue-green (breaking) Zero downtime; additive changes cover 95% of migrations
Tenant onboarding Manual provisioning Automated pipeline Automated (< 60s) Self-service is critical for SaaS growth; automation scales to 100K tenants
Data region Single region Multi-region per tenant Configurable per tenant (Enterprise) Free/Pro in default region; Enterprise chooses region for compliance
12

Reliability & fault tolerance

Reliability & Fault Tolerance

Single Points of Failure & Mitigations

Single Points of Failure & MitigationsExcalidraw diagram · editable shapes · reveal step by stepExplore

Per-Tenant SLA

Per-Tenant SLAExcalidraw diagram · editable shapes · reveal step by stepExplore
13

Production architecture

Full System Architecture (Production-Grade)

Full System Architecture (Production-Grade)Excalidraw diagram · editable shapes · reveal step by stepExplore
14

Further exploration

Workshop note · added for the website’s common reading format

Rebuild Multi-Tenant SaaS Platform from memory, then change one assumption: ten times more traffic, a new region, or a stricter consistency requirement. Which component must change first—and which does not?

Compare Application services with the same boundary in a related design. Write down one alternative you rejected, what it would simplify, and when you would choose it instead. Follow the source link at the end of this article to explore the original document.

15

Interview playbook

Interview Tips

  1. Start with the tenant isolation spectrum — "The fundamental decision is shared vs dedicated infrastructure. Draw the spectrum: shared tables (cheapest, highest density) → schema-per-tenant → database-per-tenant → VM-per-tenant (most isolated, most expensive). Most SaaS uses a hybrid: shared for small tenants, dedicated for enterprise."

  2. Row-Level Security is the defense-in-depth hero — "Even if application code forgets WHERE tenant_id = ?, PostgreSQL RLS enforces it at the database level. This is the safety net that prevents cross-tenant data leaks. Application-level filtering is the primary mechanism; RLS catches bugs."

  3. Noisy neighbor is the #1 operational challenge — Don't just say "rate limiting." Explain the full stack: per-tenant API rate limits, per-tenant DB connection pools, per-tenant query timeouts, per-tenant background job quotas, and circuit breakers for runaway tenants. One misconfigured tenant's automation must not degrade the platform.

  4. Tenant context must flow through EVERY layer — "Tenant resolution at the gateway is just the start. The tenant_id must propagate to: service layer (request context), database (RLS session variable), cache (key prefix), background jobs (payload), events (metadata), logs (field), and traces (tag). If ANY layer lacks tenant context, you have an isolation gap."

  5. Hybrid data model is the pragmatic answer — "100K tenants on dedicated databases = 100K DB instances = operational nightmare + cost explosion. Shared pool with tenant_id column for 98% of tenants, dedicated instances for the 2% enterprise tenants who need compliance/isolation. Upgrade path: migrate data from shared to dedicated when tenant upgrades."

  6. Feature flags drive the upgrade funnel — "Free user clicks 'Analytics' → '403 Upgrade to Pro.' This is the SaaS growth engine. Feature flags per plan, but also per-tenant (beta features to select tenants). The feature flag service is a revenue tool, not just a dev tool."

  7. Billing metering is a distributed systems problem — "Track usage in Redis (real-time, for rate limiting) → flush to ClickHouse (durable, for billing). Monthly aggregation → invoice generation → Stripe charge. Overage pricing: base + (usage - included) × overage_rate. Get billing wrong = lose trust."

  8. Schema migrations across 100K tenants — "Additive changes only (ADD COLUMN nullable) via online DDL — zero downtime, all tenants simultaneously. Breaking changes: blue-green migration (new table, backfill, dual-write, cutover). Never ALTER TABLE NOT NULL on a 100K-tenant shared table."

  9. Tenant deletion must be thorough (GDPR) — Explain: 30-day grace period, then hard delete from ALL stores (DB, cache, search index, blob storage, backups age out). Generate deletion certificate. This is legally required and builds trust.

  10. End with the business model connection — "Multi-tenancy isn't just an architecture pattern — it's the SaaS business model. Shared infrastructure = low cost per tenant = freemium viable. Per-tenant metering = usage-based pricing. Feature gating = upgrade funnel. Dedicated tier = enterprise contracts. The architecture enables the business."