Multi-tenancy & Workspaces
Serve multiple customers / organizations from one product while keeping their data, config, and users isolated.
Options
Data Isolation Model*
Tenant Identification*
Per-tenant Configuration
Decision Points
Do your target customers contractually require data residency or physical data isolation?
If yes
Plan database-per-tenant (or at minimum region-pinned schemas) from the start.
If no
Shared DB + tenant_id is the right starting point — do not over-isolate prematurely.
Do you expect one tenant to be >20% of total load?
If yes
Consider giving large tenants their own database to prevent noisy-neighbor impact.
If no
Shared-DB + resource limits per tenant is sufficient.
Shared DB with tenant_id, schema-per-tenant, or DB-per-tenant?
If yes
Default to shared DB + tenant_id for 95% of SaaS. Move to schema-per-tenant only as a transitional step, and DB-per-tenant only when contracts demand physical isolation.
If no
If unsure, stay with shared DB + tenant_id and invest in defense-in-depth (RLS, typed repos) instead of heavier isolation.
Do you need per-tenant custom domains (vanity domains like customer.com)?
If yes
Use subdomain routing with an ACME automation layer (e.g. Caddy, cert-manager, or a managed service like Approximated) to issue TLS certs on demand.
If no
Wildcard subdomain TLS is enough — skip the cert automation complexity until a customer actually asks.
Do customers contractually require tenant-level encryption key separation (BYOK)?
If yes
Plan DB-per-tenant or envelope encryption with per-tenant KMS keys (AWS KMS, GCP KMS). Budget significant engineering for key rotation and recovery.
If no
A single application-managed encryption key is sufficient and dramatically simpler to operate.
Do tenants need branded UI (logo, colors, custom domain)?
If yes
Enable custom-branding under features-per-tenant and store theme config per tenant. Standard for B2B2C.
If no
Skip branding entirely — one less surface to test, one less config to migrate.
Do enterprise contracts require isolated backups per tenant?
If yes
Plan DB-per-tenant or schema-per-tenant so pg_dump produces a clean per-tenant artifact. Shared-schema backups cannot satisfy this.
If no
One backup pipeline across all tenants is sufficient and cheaper to operate.
Do internal admins need cross-tenant reporting (aggregate metrics across tenants)?
If yes
Shared DB makes this trivial. With DB-per-tenant, plan a separate analytics warehouse (Snowflake, BigQuery) that ingests from all tenant DBs.
If no
No extra infrastructure needed — per-tenant queries are the only access pattern.
Do tenants need self-serve data export (GDPR portability)?
If yes
Build a per-tenant export job that streams their data as JSON/CSV. Add tenant-settings to expose the trigger. Required for GDPR compliance.
If no
Skip for now — handle ad-hoc export requests manually until a customer asks.
Do your customers need to share resources across tenants (e.g. a partner sharing a project)?
If yes
Requires explicit cross-tenant ACL modeling — avoid shared-schema tenant_id filtering assumptions. Consider ReBAC (SpiceDB, OpenFGA).
If no
Strict tenant_id isolation is the right default — never cross the boundary.
Do enterprise customers ask for tenant-scoped feature flags?
If yes
Enable feature-flags under features-per-tenant. Use LaunchDarkly, Unleash, or a simple tenant-id-keyed table — inevitable by month 6.
If no
Global flags are fine initially; add per-tenant scoping when the first enterprise deal demands it.
Do you need per-tenant rate limits (prevent noisy neighbors at the API layer)?
If yes
Key rate-limit buckets on tenant_id in Redis. Expose current usage in tenant-settings so admins can see their budget.
If no
Per-user or global limits are sufficient at your current scale.
Is tenant signup self-serve or sales-gated?
If yes
Build a provisioning flow that creates tenant context, default admin, and any per-tenant resources (schemas, S3 prefixes) atomically.
If no
Manual provisioning via internal admin is fine for enterprise-only products — skip the self-serve UX.
Do you need a tenant suspension/deactivation workflow (non-payment, abuse)?
If yes
Add a tenant status field (active/suspended/deleted) checked in middleware; persist data for the grace period before hard-delete.
If no
Hard-delete on offboarding is simpler but loses any recovery window — revisit once you have paying customers.
Will you need to migrate tenants between clusters/regions (data residency, scale)?
If yes
Design tenant data as a portable unit from day one — schema-per-tenant or DB-per-tenant makes this a dump+restore rather than a surgical export.
If no
Shared-schema is fine; cross-tenant migrations become painful but not impossible.
Do you need per-tenant observability (metrics, logs tagged by tenant)?
If yes
Tag every log line, metric, and trace with tenant_id. Use Grafana/Datadog tenant dashboards so support can diagnose individual tenants.
If no
Aggregate observability is enough for small tenant counts; add tagging once you have >50 tenants.
Do you need per-tenant audit logs for GDPR data-subject requests?
If yes
Combine with the audit-logging module — ensure every audit entry carries tenant_id and build a per-tenant export endpoint.
If no
Central audit logs with tenant_id filtering cover most cases until a DSR lands.
Do you need per-tenant queues/workers for noisy-neighbor isolation in background jobs?
If yes
Partition your queue by tenant_id (separate BullMQ/SQS queues per tenant, or weighted fair scheduling). Required for any tenant running bulk imports.
If no
A shared queue with per-tenant concurrency caps is simpler and sufficient for balanced workloads.
Tradeoffs
Cheapest ops and easiest queries, but a single missed tenant_id filter is a data leak
Operational cost scales with tenant count; migrations must run against every tenant DB
Wildcard TLS cert and DNS configuration required; vanity domains compound that
Testing surface expands — every change must consider flag combinations and tenant configs
Go Deeper
Propagating tenant context safely
The single most common multi-tenant bug is executing a query without the right tenant context.