Cloud
Blog
Cloud13 min

SaaS Multi-Tenant Architecture on Kubernetes: Where to Start

Matthieu Robin15 septembre 2022

You've built a SaaS product. Now you need to handle 50 different customers on the same infrastructure without them being able to see each other's data.

Your engineering team has three options:

  • One application instance per customer (expensive, doesn't scale)
  • One database per customer (management nightmare)
  • True multi-tenancy (complex, but powerful)

Most scaling SaaS products eventually land on true multi-tenancy: one application serving multiple customers with complete isolation at every layer. Done well, it reduces operational costs by 70-80%. Done poorly, it becomes a security nightmare.

Kubernetes makes multi-tenancy easier than ever. But "easier" doesn't mean "easy." You need clear patterns for isolation, tenant management, data separation, and security. This article walks through exactly how to architect multi-tenant SaaS on Kubernetes and Hikube.

Why Multi-Tenancy on Kubernetes?

Before diving into architecture, let's clarify why Kubernetes is the right platform for multi-tenant SaaS.

Kubernetes advantages:

  • Namespace isolation: One Kubernetes namespace per tenant (or per customer tier) provides logical separation
  • Horizontal scaling: Add more replicas instantly when a customer's load increases
  • Resource quotas: Enforce memory/CPU limits per tenant, preventing one customer from drowning out others
  • Multi-region deployment: Run the same application in multiple regions with the same architecture
  • Cost efficiency: One cluster serves 100 customers instead of 100 separate deployments

Hikube advantages for SaaS:

  • Managed control plane: You don't manage Kubernetes infrastructure
  • Swiss-based infrastructure: Meet data residency requirements for European customers
  • Built-in multi-tenancy support: Namespace isolation, RBAC, network policies
  • Auto-scaling: Customers can burst without capacity planning
  • Compliance-ready: Audit logging, data encryption, security scanning included

Architecture Decision: Shared vs. Separate Infrastructure

Your first decision: shared cluster vs. separate clusters per customer tier.

Shared Cluster Model (Most Common)

All customers run on the same Kubernetes cluster. Each gets a dedicated namespace.

Advantages:

  • Lowest operational complexity
  • Best cost efficiency (one cluster overhead)
  • Easier resource utilization (unused capacity shared across tenants)
  • Simpler disaster recovery (one cluster to backup)

Disadvantages:

  • Must implement strict isolation (buggy isolation = data leak)
  • One tenant's misconfigured application can impact others (noisy neighbor problem)
  • Scaling decisions affect all tenants

When to use: Most SaaS products (especially early-stage). Cost and simplicity usually win.

Separate Cluster Per Tier

Premium customers get dedicated clusters. Standard customers share a cluster.

Advantages:

  • Premium customers don't suffer noisy neighbor issues
  • Can isolate compliance-sensitive workloads
  • Better performance guarantees for high-value customers
  • Easier multi-region deployment per tier

Disadvantages:

  • Operational complexity increases (now managing 3-5 clusters instead of 1)
  • Cost increases (each cluster has overhead)
  • Upgrade and maintenance is more complex

When to use: Mature SaaS products with clear tier structure ($100K+ ARR customers warrant dedicated clusters).

Namespace Isolation: The Foundation

Kubernetes namespaces provide logical isolation. They're not security boundaries by themselves, so you must add layers.

Basic Namespace Structure

production
├── tenant-acme-corp
├── tenant-globex
├── tenant-initech
└── shared-services (monitoring, logging)

staging
├── tenant-acme-corp
├── tenant-globex
└── ...

Each tenant gets:

  • A dedicated namespace in production
  • A dedicated namespace in staging
  • Isolated deployments, services, secrets, and persistent volumes

RBAC for Tenant Isolation

Role-Based Access Control (RBAC) prevents a tenant's service account from accessing another tenant's resources.

Example RBAC rule for tenant-acme-corp:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: tenant-acme-corp
  name: tenant-acme-reader
rules:
- apiGroups: [""]
  resources: ["pods", "services", "configmaps", "secrets"]
  verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  namespace: tenant-acme-corp
  name: tenant-acme-binding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: tenant-acme-reader
subjects:
- kind: ServiceAccount
  name: tenant-acme-app
  namespace: tenant-acme-corp

This ensures the tenant-acme-app service account can only access resources in its own namespace.

Network Policies: Block Cross-Tenant Traffic

Kubernetes Network Policies are firewalls between namespaces.

Example: Block all ingress except within namespace

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  namespace: tenant-acme-corp
  name: deny-cross-namespace
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector: {}
      namespaceSelector:
        matchLabels:
          name: tenant-acme-corp

This prevents pods in other namespaces from reaching tenant-acme-corp's pods.

You must also explicitly allow ingress traffic from your ingress controller. Most teams use a shared ingress controller that routes traffic based on hostname or path.

Tenant Data Strategies

Where does tenant data live? Your database strategy drives everything else.

Strategy 1: Shared Database, Separate Schemas

All tenants use the same database (e.g., PostgreSQL), but each tenant has a separate schema.

Example:

postgres
├── schema: acme_corp (all acme_corp tables)
├── schema: globex (all globex tables)
├── schema: initech (all initech tables)
└── schema: shared (reference data)

Advantages:

  • Operationally simple: One database to manage
  • Easy to scale compute separately from data
  • Data isolation is enforced by the database
  • Row-level security (RLS) adds additional tenant isolation in the application layer

Disadvantages:

  • Single database failure affects all tenants
  • Expensive for very large customers
  • Harder to scale individual tenants (all tenants share database compute)
  • Backup/restore is all-or-nothing

When to use: Most SaaS products in early-to-mid stages.

Strategy 2: Shared Database, Row-Level Filtering

All tenants in one schema. The application filters rows by tenant ID.

SELECT * FROM orders WHERE tenant_id = 'acme-corp'

Advantages:

  • Lowest operational complexity
  • Easy to debug (all data in one schema)
  • Best cost efficiency
  • Efficient queries (database query planner sees all data)

Disadvantages:

  • Massive security risk if filtering is buggy (one bug = data leak across all customers)
  • Requires disciplined application code (every query must filter by tenant)
  • Hard to implement row-level security (RLS) consistently
  • Complex data migrations

When to use: Only if your engineering team is highly disciplined and security-conscious. Not recommended for most teams.

Strategy 3: Separate Database Per Tenant

Each customer gets their own database instance.

Advantages:

  • Ultimate data isolation (database-level separation)
  • Scale individual customers independently
  • Easy compliance (customer data is literally separate)
  • Simple disaster recovery (backup one customer's database)

Disadvantages:

  • Operational nightmare: Managing 1,000 databases for 1,000 customers
  • Cost multiplier: Each database has overhead (memory, storage, backups)
  • Schema migrations require coordinating across all databases
  • Complex to scale: Adding one database per new customer is inefficient

When to use: Only for high-value customers (dedicated instances) or compliance-sensitive workloads. Never for all customers.

Recommended Hybrid Approach

Use Strategy 1 (separate schemas in shared database) for most customers. Use Strategy 3 (separate databases) for your top 5-10 highest-value customers.

This gives you:

  • Cost efficiency for most customers (shared database)
  • Premium experience for high-value customers (dedicated database)
  • Clear upgrade path (customer success can move customers to dedicated database)

Tenant Management Pattern: The Tenant Controller

Production multi-tenant systems usually add a "tenant controller", an automated process that manages tenant lifecycle.

The tenant controller handles:

  • Creating a new Kubernetes namespace when a customer signs up
  • Provisioning database schema for the customer
  • Creating service accounts and RBAC rules
  • Configuring network policies
  • Setting resource quotas
  • Generating TLS certificates for the tenant's custom domain
  • Updating DNS when the customer adds a custom domain

Example workflow:

Customer Signs Up
    ↓
POST /api/tenants (internal API)
    ↓
Tenant Controller Receives Event
    ↓
Create Namespace (tenant-acme-corp)
Create ServiceAccount (tenant-acme-app)
Create RBAC Role + RoleBinding
Create NetworkPolicy
Create ResourceQuota (CPU, Memory limits)
Provision Database Schema
Create Secret with DB connection string
Create Ingress Rule for custom domain
    ↓
Tenant Ready

Most teams implement this as a Kubernetes Operator (using frameworks like kubebuilder). However, a simpler approach (webhook + automation script) works fine for smaller SaaS products.

Resource Quotas: Preventing the Noisy Neighbor Problem

Without quotas, one tenant's runaway application can consume all cluster resources, causing other tenants' applications to fail.

Example ResourceQuota for a standard tenant:

apiVersion: v1
kind: ResourceQuota
metadata:
  namespace: tenant-acme-corp
  name: tenant-quota
spec:
  hard:
    requests.cpu: "10"
    requests.memory: "20Gi"
    limits.cpu: "20"
    limits.memory: "40Gi"
    pods: "100"
    services.loadbalancers: "2"
    persistentvolumeclaims: "10"
  scopeSelector:
    matchExpressions:
    - operator: In
      scopeName: PriorityClass
      values: ["default"]

This prevents a tenant from requesting more than 10 CPU cores or 20GB RAM during normal operation. Burst is allowed up to 20 cores/40GB via limits.

Resource quota strategy by tier:

  • Starter tier: 2 cores / 4GB memory
  • Standard tier: 5 cores / 10GB memory
  • Premium tier: 20 cores / 50GB memory (or dedicated cluster)

Tie these limits to your pricing model. If a customer needs more resources, they upgrade their plan.

Horizontal Scaling for Multi-Tenant Workloads

Kubernetes horizontal pod autoscaling (HPA) works well for multi-tenant systems, but you need careful configuration.

Challenge: If you have 100 tenants and one tenant experiences a spike, the HPA will scale up replicas. But now you're running extra capacity for that one tenant while other tenants sit idle.

Solution: Tenant-aware scaling

Some teams use custom metrics to scale based on per-tenant load:

apiVersion: autoscaling.k8s.io/v2
kind: HorizontalPodAutoscaler
metadata:
  namespace: tenant-acme-corp
  name: app-autoscaler
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: tenant-app
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Pods
    pods:
      metric:
        name: http_requests_per_second
      target:
        type: AverageValue
        averageValue: "1k"

This scales the tenant's deployment when they exceed 1,000 requests per second.

For truly multi-tenant systems: Consider running a shared application pool with multiple tenants per pod, using application-level routing to distribute requests. This maximizes resource utilization and minimizes overhead.

Ingress Routing: Getting Traffic to the Right Tenant

With multiple tenants, you need an Ingress controller that routes traffic by hostname or path.

Pattern 1: Hostname-based routing (Most Common)

Each tenant has a subdomain or custom domain:

  • acme-corp.app.example.com → routes to tenant-acme-corp namespace
  • globex.example.com → routes to tenant-globex namespace
  • customer.example.com (custom domain) → routes to appropriate tenant namespace

Ingress configuration:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  namespace: tenant-acme-corp
  name: tenant-acme-ingress
spec:
  ingressClassName: nginx
  rules:
  - host: acme-corp.app.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: app-service
            port:
              number: 80
  - host: custom.acmecorp.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: app-service
            port:
              number: 80

Pattern 2: Path-based routing

All tenants behind one domain, separated by path:

  • app.example.com/acme-corp/ → tenant-acme-corp
  • app.example.com/globex/ → tenant-globex

This is simpler but requires application changes to handle path prefixes.

Security Considerations

Multi-tenant systems are attractive targets for attackers. Breaches are expensive. Add these security layers:

1. Network Policies (Already Covered)

Block cross-namespace traffic by default.

2. Pod Security Policies / Pod Security Standards

Prevent tenants from running privileged containers that could escape isolation.

apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: restricted
spec:
  privileged: false
  allowPrivilegeEscalation: false
  requiredDropCapabilities:
    - ALL
  volumes:
    - 'configMap'
    - 'emptyDir'
    - 'projected'
    - 'secret'
    - 'downwardAPI'
    - 'persistentVolumeClaim'
  hostNetwork: false
  hostIPC: false
  hostPID: false
  runAsUser:
    rule: 'MustRunAsNonRoot'
  seLinux:
    rule: 'MustRunAs'

3. Secret Management

Never store database credentials in ConfigMaps. Use Kubernetes Secrets or external secret managers (HashiCorp Vault, AWS Secrets Manager).

apiVersion: v1
kind: Secret
metadata:
  namespace: tenant-acme-corp
  name: db-credentials
type: Opaque
stringData:
  connection-string: "postgresql://user:password@db.internal:5432/acme_corp"

Reference it in your application:

env:
- name: DATABASE_URL
  valueFrom:
    secretKeyRef:
      name: db-credentials
      key: connection-string

4. Audit Logging

Log all access to sensitive resources. Kubernetes audit logging is built-in.

5. Regular Security Scanning

Use container image scanning (Trivy, Snyk) to detect vulnerabilities in your application images before they reach production.

Monitoring and Observability for Multi-Tenant Systems

With multiple tenants, you need to track metrics per-tenant to understand costs and performance.

Key metrics to track:

  • CPU/memory usage per tenant
  • Request count per tenant
  • Database query count per tenant
  • Storage usage per tenant
  • Error rate per tenant
  • API latency (p50, p95, p99) per tenant

Example Prometheus query (per-tenant CPU usage):

sum(rate(container_cpu_usage_seconds_total{namespace=~"tenant-.*"}[5m])) by (namespace)

This gives you CPU usage breakdown by tenant namespace.

Most teams use Prometheus + Grafana for metrics, and optionally add application-level observability (Datadog, New Relic) for deeper insights.

Multi-Region Deployment

As your SaaS grows, you'll need multiple regions for latency and disaster recovery.

Multi-region pattern for multi-tenant:

Region: US-East
├── Kubernetes Cluster (Hikube)
│   ├── Namespace: tenant-acme-corp (primary)
│   ├── Namespace: tenant-globex
│   └── Database: Primary (PostgreSQL)

Region: EU-West
├── Kubernetes Cluster (Hikube)
│   ├── Namespace: tenant-acme-corp (replica)
│   ├── Namespace: tenant-globex (replica)
│   └── Database: Replica (Read-only)

Region: APAC
├── Kubernetes Cluster (Hikube)
│   └── [Same pattern]

Route traffic based on geography (using DNS geolocation or a global load balancer). This ensures:

  • Low latency: Customers connect to nearest region
  • Data residency: Customer data stays in their region (compliance)
  • Disaster recovery: If one region fails, traffic moves to others

Deployment Patterns on Hikube

Pattern 1: GitOps with Flux

Use Flux to automatically deploy changes to Hikube as you commit to Git.

apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: GitRepository
metadata:
  namespace: flux-system
  name: multi-tenant-repo
spec:
  interval: 1m
  url: https://github.com/your-company/saas-infra
  ref:
    branch: main

apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  namespace: flux-system
  name: multi-tenant-app
spec:
  interval: 10m
  sourceRef:
    kind: GitRepository
    name: multi-tenant-repo
  path: ./deployments/multi-tenant
  prune: true
  wait: true

Benefits: Declarative infrastructure, easy auditing, automated rollbacks.

Pattern 2: Helm for Tenant Provisioning

Use Helm to template tenant namespaces and deploy them consistently.

helm install tenant-acme-corp ./tenant-chart \
  --namespace tenant-acme-corp \
  --create-namespace \
  --set tenantName=acme-corp \
  --set tenantId=cust-12345 \
  --set tier=premium

This automates namespace creation, RBAC, quotas, and application deployment.

Cost Optimization for Multi-Tenant SaaS

Multi-tenancy is cost-efficient, but you can optimize further:

1. Right-size resource requests/limits Don't allocate more resources than tenants actually need. Monitor and adjust.

2. Use cluster autoscaling Let Hikube scale the cluster up/down based on actual demand.

3. Use spot/preemptible instances for non-critical workloads Dev/staging environments can run on cheaper spot instances.

4. Implement tenant-based chargeback Track cost per tenant (CPU, storage, networking) and charge accordingly. This incentivizes customers to optimize their usage.

5. Use reserved instances for baseline capacity Commit to a baseline and use on-demand for burst.

Getting Started: Your First Multi-Tenant Deployment on Hikube

  1. Design your tenant model: Decide on shared cluster vs. separate clusters, and database strategy (shared schemas in most cases).

  2. Create namespace templates: Build Kubernetes manifests for namespace, RBAC, quotas, network policies.

  3. Set up ingress routing: Deploy an Ingress controller (Nginx or Traefik) configured for hostname-based routing.

  4. Implement tenant provisioning: Build automation to create namespaces, schemas, and secrets when customers sign up.

  5. Add monitoring: Set up per-tenant metrics in Prometheus/Grafana.

  6. Test isolation: Deliberately try to access other tenants' data. Ensure your isolation works.

  7. Plan for upgrades: Define how you'll upgrade applications per-tenant without disrupting others.

Conclusion

Multi-tenant SaaS on Kubernetes is powerful but requires careful architecture. The good news: Kubernetes provides all the tools you need (namespaces, RBAC, network policies, resource quotas, auto-scaling).

Quick recap:

  • Use a shared Kubernetes cluster for most customers
  • Isolate tenants via namespaces, RBAC, and network policies
  • Store data in shared database with separate schemas (for most customers)
  • Implement resource quotas to prevent noisy neighbor issues
  • Monitor per-tenant metrics to understand costs
  • Add security layers: pod security policies, audit logging, secrets management

The complexity is not in Kubernetes itself. It's in building the automation (tenant controller) that manages the multi-tenant lifecycle. Invest in this early, and your SaaS scales smoothly.


Ready to deploy multi-tenant SaaS on Kubernetes? Hidora's consulting team can help you architect, secure, and optimize your multi-tenant system on Hikube. We work with SaaS companies at every scale. Hikube Consulting Services · Managed Kubernetes · Managed Services

Does this article resonate?

Hidora can support you on this topic.

Need support?

Let's talk about your project. 30 minutes, no strings attached.