Multi-Tenancy in SaaS: A Complete Code Walkthrough

π Read Time: ~20 mins | π¨βπ» Skill Level: Intermediate
π’ Introduction: What is Multi-Tenancy?
Imagine an apartment building vs. individual houses.
In single tenancy, every customer gets their own "house" (dedicated infrastructure). Expensive, hard to maintain!
In multi-tenancy, customers share the same "building" (app instance), but each has their own isolated space (data, configs, and performance).
This is how SaaS companies like Slack, Shopify, and Salesforce serve millions efficiently!
β Why Should You Care?
If you're building a SaaS product, multi-tenancy helps you:
β Reduce costs (no separate servers per customer)
β Scale effortlessly (one codebase, shared resources)
β Deploy updates faster (no version chaos)
But how does it work? Letβs break it down.
π§ How Multi-Tenancy Works: 3 Key Approaches
1οΈβ£ Database-Level Isolation
Problem: How do you store tenant data securely?
Two Strategies:
Separate Databases (Best isolation, but expensive)
Shared Database, Separate Schemas (Most common)
Example: A PostgreSQL DB where each tenant has its schema:
-- Tenant 1 Schema
CREATE SCHEMA tenant1;
CREATE TABLE tenant1.users (id SERIAL, name TEXT);
-- Tenant 2 Schema
CREATE SCHEMA tenant2;
CREATE TABLE tenant2.users (id SERIAL, name TEXT);
β Pros: Strong isolation, easy backups.
β Cons: More complex queries.
2οΈβ£ Application-Level Isolation
Problem: How does the app know which tenant is accessing it?
Solutions:
URL-Based Routing (
tenant1.yoursaas.com
,tenant2.yoursaas.com
)Header/Token-Based (JWT contains
tenant_id
)
Example in Node.js:
// Middleware to detect tenant
app.use((req, res, next) => {
const tenantId = req.headers['x-tenant-id'] || req.hostname.split('.')[0];
req.tenant = getTenantDB(tenantId); // Switches DB connection
next();
});
β Pros: Flexible, works with any backend.
β Cons: Requires careful session handling.
3οΈβ£ Kubernetes-Based Isolation (Infrastructure Level)
Problem: How do you deploy multi-tenant apps in Kubernetes without leaks?
Solution: Namespaces + Network Policies
Each tenant gets a dedicated namespace.
Network Policies block cross-tenant traffic.
Resource Quotas prevent noisy neighbors.
Example Kubernetes Setup:
# tenant1-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: tenant1
# tenant1-network-policy.yaml (No cross-namespace traffic!)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-cross-tenant
namespace: tenant1
spec:
podSelector: {}
policyTypes: ["Ingress"]
ingress:
- from:
- namespaceSelector:
matchLabels:
name: tenant1
β Pros: Strong security, scalable.
β Cons: Needs Kubernetes expertise.
π Repository Structure Overview
Here's what you'll find in the repo (kubernetes-multitenant)
kubernetes-multi-tenant/
βββ manifests/
β βββ namespaces/
β β βββ tenant1-namespace.yaml
β β βββ tenant2-namespace.yaml
β βββ network-policies/
β β βββ deny-cross-tenant.yaml
β βββ deployments/
β β βββ tenant1-deployment.yaml
β β βββ tenant2-deployment.yaml
β βββ services/
β βββ tenant1-service.yaml
β βββ tenant2-service.yaml
βββ README.md
βΈοΈ Kubernetes Configuration Breakdown
1. Namespace Isolation
File: manifests/namespaces/tenant1-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: tenant1
labels:
tenant: tenant1
What This Does:
Creates a dedicated namespace for Tenant1
Applies a
tenant: tenant1
label for easy selectionRepeat for Tenant2 with identical structure but
tenant2
values
2. Network Policy Enforcement
File: manifests/network-policies/deny-cross-tenant.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-cross-tenant
namespace: tenant1
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
tenant: tenant1
Key Security Features:
podSelector: {}
applies to all pods in the namespaceOnly allows ingress traffic from same-tenant namespaces
Effectively blocks all cross-tenant communication
3. Tenant Deployments
File: manifests/deployments/tenant1-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: tenant1-app
namespace: tenant1
spec:
replicas: 2
selector:
matchLabels:
app: multi-tenant-app
tenant: tenant1
template:
metadata:
labels:
app: multi-tenant-app
tenant: tenant1
spec:
containers:
- name: app
image: nginx:alpine
ports:
- containerPort: 80
Important Notes:
Deploys to
tenant1
namespace specificallyUses common
app: multi-tenant-app
label but tenant-specifictenant: tenant1
labelSimple nginx container for demonstration
4. Service Exposure
File: `manifests/services/tenant1-service.yaml**
apiVersion: v1
kind: Service
metadata:
name: tenant1-service
namespace: tenant1
spec:
selector:
app: multi-tenant-app
tenant: tenant1
ports:
- protocol: TCP
port: 80
targetPort: 80
Service Discovery:
Other Tenant1 pods can access via
tenant1-service.tenant1.svc.cluster.local
Completely isolated from Tenant2's services
π Deploying the Demo
Follow these exact steps from the repository:
# Apply all configurations
kubectl apply -f manifests/namespaces/
kubectl apply -f manifests/network-policies/
kubectl apply -f manifests/deployments/
kubectl apply -f manifests/services/
# Verify deployment
kubectl get pods -n tenant1
kubectl get pods -n tenant2
# Test isolation (should fail)
kubectl exec -it -n tenant1 $(kubectl get pod -n tenant1 -o name | head -1) -- curl http://tenant2-service.tenant2.svc.cluster.local
π Testing Multi-Tenant Isolation
Verify Namespace Isolation:
kubectl get pods --namespace=tenant1 kubectl get pods --namespace=tenant2
Check Network Policies:
kubectl describe networkpolicy deny-cross-tenant -n tenant1
Test Cross-Tenant Communication:
# This should fail due to network policies kubectl exec -it -n tenant1 $(kubectl get pod -n tenant1 -o name | head -1) -- curl http://tenant2-service.tenant2.svc.cluster.local
π‘ Key Takeaways from the Code
Namespace Boundaries are your first line of defense
Network Policies enforce strict isolation
Labeling Strategy (
tenant: tenant1
) enables precise resource selectionService Discovery remains tenant-specific through DNS
π Next Steps with This Repository
Add Resource Quotas to prevent noisy neighbors
Implement Tenant-Specific ConfigMaps for customization
Add Database Backends with tenant isolation
Deploy a Real Application instead of nginx
π¬ Discussion Points
After exploring the actual repository code:
What other security layers would you add?
How might you modify this for hybrid tenancy models?
What monitoring would you implement across tenants?
Try the demo yourself and share your experiences in the comments!
#Kubernetes #MultiTenancy #DevOps #CloudNative #SaaSDevelopment
Subscribe to my newsletter
Read articles from Vitthal Kushe directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
