Multi-Tenant SaaS Isolation Patterns
Part of Kubernetes
- SaaS
- Architecture
- Security
White-label multi-tenant SaaS sounds simple until tenant A's batch export saturates shared connection pools and tenant B's checkout page times out. At Edmingle-a platform where each customer runs their own branded learning portal-the isolation model you pick on day one becomes migration debt on day five hundred.
Isolation is not binary. It is a spectrum from shared schema with row-level security to dedicated clusters per enterprise customer. The right pattern depends on contract SLAs, compliance boundaries, and how much noisy-neighbor risk your smallest tenant can tolerate subsidizing for your largest.
The Isolation Spectrum
Every multi-tenant platform sits somewhere on this continuum. Moving right increases cost and operational overhead but decreases blast radius.
Cost ◄──────────────────────────────────────────────► Isolation Shared DB + tenant_id Schema-per-tenant DB-per-tenant Cluster-per-tenant Shared K8s namespace Namespace + NetworkPolicy Dedicated node pool Dedicated EKS cluster Single deployment Single deployment + quotas Per-tenant microservice Full stack fork
| Pattern | Typical customer | Noisy neighbor risk | Ops burden |
|---|---|---|---|
| Shared everything + RLS | SMB, high volume | High | Low |
| Schema or DB isolation | Mid-market | Medium | Medium |
| Namespace + dedicated resources | Enterprise | Low | Medium-high |
| Dedicated cluster / VPC | Regulated enterprise | Minimal | High |
Edmingle runs a hybrid: SMB tenants share pooled infrastructure with strict quotas; enterprise tenants get dedicated RDS instances and isolated Kubernetes namespaces with dedicated node groups.
Data Layer Boundaries
The database is where tenant leaks hurt most. Row-level security (RLS) in PostgreSQL is effective when every query path goes through an ORM middleware that injects tenant_id-and when you have linting that rejects raw SQL without tenant scope.
-- PostgreSQL RLS example
ALTER TABLE courses ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON courses
USING (tenant_id = current_setting('app.tenant_id')::uuid);
-- Set per-request in application middleware
SET LOCAL app.tenant_id = '550e8400-e29b-41d4-a716-446655440000';Schema-per-tenant scales to hundreds of tenants on a single Postgres cluster before catalog bloat becomes painful. Database-per-tenant simplifies backup/restore per customer and makes enterprise compliance conversations easier, but connection pooling and migration orchestration get harder.
- Never trust client-supplied tenant_id-derive it from signed JWT claims or subdomain resolution.
- Run periodic chaos tests that attempt cross-tenant reads with forged headers.
- Encrypt tenant data at rest with KMS keys; enterprise tenants may require customer-managed keys (CMK).
- Audit logs must include tenant_id on every mutation for SOC2 and GDPR data subject requests.
Compute and Network Isolation on Kubernetes
For a white-label platform, compute isolation usually means Kubernetes namespaces per tenant tier, not per SMB customer. Enterprise tenants get dedicated namespaces with ResourceQuotas, LimitRanges, and NetworkPolicies.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-cross-namespace
namespace: tenant-acme
spec:
podSelector: {}
policyTypes: [Ingress, Egress]
ingress:
- from:
- namespaceSelector:
matchLabels:
tenant: acme
egress:
- to:
- namespaceSelector:
matchLabels:
tenant: acmeRequest flow (enterprise tenant)
────────────────────────────
tenant.acme.edmingle.com
│
▼
Ingress (dedicated ALB rule)
│
▼
namespace: tenant-acme
├── api pods (ResourceQuota: 8 CPU)
├── worker pods (Keda scaled)
└── NetworkPolicy: deny other namespacesShared ingress with host-based routing is fine for SMB. Enterprise often wants dedicated subdomains with WAF rules, IP allowlists, and separate TLS certificates stored in tenant-scoped secrets.
Noisy Neighbor Controls Beyond Kubernetes
Kubernetes quotas cap CPU and memory but not S3 API calls, Redis command rates, or outbound email volume. Platform-level rate limiting must sit at the edge and at shared service boundaries.
- API gateway rate limits per tenant API key and per IP.
- Redis key prefixes with tenant-scoped memory policies; consider dedicated Redis for enterprise.
- SQS or Kafka consumer concurrency caps per tenant for async workloads.
- Fair queuing on batch jobs-one tenant's CSV export should not block another's certificate generation.
We expose tenant-level usage metrics to customer admins. Transparency reduces support tickets and gives sales a upsell path to dedicated tiers.
White-Label Identity and Configuration
Edmingle-style platforms need per-tenant branding, SSO, and feature flags without redeploying. Store tenant config in a fast-read layer (Redis or DynamoDB) keyed by subdomain, with Git as the source of truth for structural changes.
| Config type | Storage | Propagation |
|---|---|---|
| Branding (logo, colors) | CDN + tenant config service | Cache TTL 5 min |
| SSO (SAML/OIDC) | Tenant DB + secrets manager | Immediate on save |
| Feature flags | LaunchDarkly or open-source equivalent | Real-time stream |
| Custom domains | Ingress controller + cert-manager | DNS validation async |
Custom domain support is an isolation concern: misconfigured cert-manager or shared ingress can serve tenant A's content on tenant B's domain. Automate verification and block go-live until DNS and TLS checks pass.
Migration Between Tiers
Customers upgrade from shared to dedicated. Without a migration playbook, you fork schemas manually and hope.
Design for export/import from day one: tenant-scoped backup jobs, idempotent data migration scripts, and a runbook that includes DNS cutover, cache invalidation, and rollback via feature flag.
The cost of migrating a tenant from shared to dedicated is dominated by data volume and downtime tolerance-not Kubernetes YAML. Invest in offline migration tooling before enterprise sales asks for it.
Engineering Takeaways
- Isolation is a spectrum; Edmingle-style white-label SaaS typically runs hybrid tiers rather than one pattern for all customers.
- Data leaks are the highest-severity failure-RLS helps but requires enforced query discipline and regular penetration testing.
- Kubernetes namespaces and NetworkPolicies handle compute boundaries; shared services (Redis, queues, email) need separate rate limits.
- Enterprise tenants expect dedicated resources, custom domains, and CMK options-design upsell paths into the architecture early.
- Tenant identity must come from trusted claims, never from client-supplied headers alone.
- Noisy neighbor mitigation spans edge rate limits, fair queuing, and per-tenant observability dashboards.
- Build tier migration tooling before you need it; manual forks do not scale past ten enterprise customers.
Trade-offs I Would Consider
Option: Shared infrastructure with RLS and quotas
- Pros: Lowest cost per tenant, simplest ops, fastest onboarding for SMB customers.
- Cons: Highest noisy-neighbor risk, hardest enterprise compliance conversations, blast radius on misconfiguration.
Option: Dedicated DB + namespace per enterprise tenant
- Pros: Strong isolation story, easier per-tenant backup/restore, clear upsell pricing model.
- Cons: Higher baseline cost, migration complexity, more Terraform/Helm permutations to maintain.
What I Would Do in Production
Enforce tenant_id in middleware at the API layer; add integration tests that assert 403 on cross-tenant resource IDs.
Run ResourceQuotas and LimitRanges on every tenant namespace; alert when any tenant exceeds 80% of quota for three consecutive days.
Ship tenant-scoped dashboards (request rate, error rate, queue lag) to customer admins and internal support.
Maintain a tier matrix document: which isolation guarantees apply to Starter, Business, and Enterprise-sales and support must cite the same doc.
Automate custom domain provisioning with cert-manager and pre-flight DNS checks; never manually edit ingress for production tenants.
Quarterly game day: simulate one tenant's traffic spike and verify others stay within SLO.
FAQ
Q: When should we offer dedicated clusters?
When contract language requires data residency, customer-managed keys, or air-gapped networking-or when a single tenant's load consistently exceeds 30% of shared cluster capacity.
Q: Is schema-per-tenant worth it over RLS?
At 50-200 tenants with similar schemas, schema-per-tenant simplifies per-tenant backup and reduces RLS foot-gun risk. Above that, operational overhead often pushes teams back toward RLS with strong guardrails.
Q: How do we handle tenant offboarding?
Soft-delete with retention period, export bundle to S3, revoke API keys and SSO, then hard-delete after legal hold clears. Automate the checklist-manual offboarding causes orphaned data and billing disputes.
Q: Can we use a single Redis cluster?
Yes for SMB tiers with key prefixes and memory limits. Enterprise tenants doing high-throughput caching or session storage often warrant dedicated ElastiCache clusters or at least dedicated shards.
Q: What about multi-region tenants?
Treat region as another isolation boundary. Data residency requirements mean the tenant's primary data never leaves the region-replicate read-only analytics separately with explicit consent.
Q: How do white-label mobile apps change isolation?
Each app store bundle may use tenant-specific API endpoints and keys. Build-time config is not enough-runtime tenant resolution from deep links and OAuth redirects must stay consistent with web isolation rules.
Related articles