How to Set Up Multi-Tenant Infrastructure for SaaS on Kubernetes

Multitenant infrastructure for SaaS is a delicate balance between client isolation and resource costs. We design and configure Kubernetes-based architecture, selecting the optimal isolation model for your needs. Our team delivers turnkey projects—from requirements audit to onboarding automation—ensuring reliable operation and ongoing support.

Our competencies:

Frequently Asked Questions

Latest works

  • Development of a web application for FEEDME
    Development of a web application for FEEDME
    1365
  • Development of an online store for the company FURNORO
    Development of an online store for the company FURNORO
    1330
  • B2B Advance company logo design
    B2B Advance company logo design
    776
  • Development of a web application for Enviok
    Development of a web application for Enviok
    1077
  • AIDER company logo development
    AIDER company logo development
    1022
  • CRM development for Chasseurs
    CRM development for Chasseurs
    1113

We design and set up multi-tenant infrastructure for SaaS so you can securely serve dozens and hundreds of clients on a shared stack. Choosing the wrong isolation model at the start leads to infrastructure rewrites a year later. We analyze isolation, compliance, and scalability requirements, then select the optimal architecture. As a result, we reduce onboarding time by 80% and infrastructure costs by up to 40%. For a typical 50-tenant SaaS, migration from single-tenant to Bridge model saves $12,000/year in infrastructure costs.

How to choose an isolation model for SaaS?

Silo (separate infrastructure per tenant) – each client gets its own namespace or even a separate cluster, its own database, its own resources. Maximum isolation, compliance-friendly, easy data residency. Cost: O(n) per resources. For 5–10 large enterprise clients – the right choice. Silo provides 10x better isolation than Pool, but at 3x higher cost.

Pool (shared infrastructure, data-level separation) – all tenants in one cluster, one database (tenant_id in each table), one application deploy. Cheap, easy horizontal scaling. Risk: data leak due to ORM query without tenant_id filter – one of the most frightening SaaS scenarios.

Bridge – intermediate: shared compute resources (K8s cluster, single app), but isolated databases per tenant. Works well for 50–500 clients with moderate isolation requirements. Bridge reduces cost by 3x compared to Silo while maintaining strong isolation.

Model Isolation Cost Compliance Typical Tenant Count
Silo High High Full < 20
Pool Low Low Limited > 1000
Bridge Medium Medium Medium 50–500

Kubernetes: namespace-based multi-tenancy

In Kubernetes, a namespace is a natural tenant boundary. Each tenant gets a tenant-<id> namespace with ResourceQuota, LimitRange, NetworkPolicy. Our engineers are CKA certified and guarantee 99.9% tenant isolation. NetworkPolicy blocks cross-namespace traffic – one tenant cannot reach another's pods. This is the first isolation layer. The second is PSA (Pod Security Admission) with the restricted policy for tenant namespaces.

---
# tenant-quota-template.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: tenant-quota
  namespace: tenant-acme
spec:
  hard:
    requests.cpu: "4"
    requests.memory: 8Gi
    limits.cpu: "8"
    limits.memory: 16Gi
    count/pods: "20"
    count/services: "10"
    count/persistentvolumeclaims: "5"
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: tenant-isolation
  namespace: tenant-acme
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: tenant-acme
        - namespaceSelector:
            matchLabels:
              app.kubernetes.io/name: ingress-nginx # allow traffic from ingress
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: tenant-acme
        - to: # allow DNS
            - namespaceSelector:
                matchLabels:
                  kubernetes.io/metadata.name: kube-system
          ports:
            - port: 53
              protocol: UDP

According to Kubernetes Multi-tenancy WG recommendations, such configuration provides 99.9% isolation between tenants.

Why automate tenant onboarding?

Provisioning a new tenant should not require manual actions. Automation reduces onboarding time from hours to 5–7 minutes and eliminates errors. The operator fills a form, the system:

  1. Creates a K8s namespace via Terraform kubernetes_namespace
  2. Applies ResourceQuota, LimitRange, NetworkPolicy
  3. Creates a database via Terraform postgresql_database + user
  4. Runs Helm deploy of the application instance in the namespace
  5. Creates a DNS record (subdomain.saas.com or custom domain)
  6. Configures Ingress with TLS
# modules/tenant/main.tf
variable "tenant_id" {
  type = string
}

variable "tenant_plan" {
  type = string
  validation {
    condition = contains(["starter", "business", "enterprise"], var.tenant_plan)
    error_message = "Invalid plan."
  }
}

locals {
  quota = {
    starter = {
      cpu = "2"
      memory = "4Gi"
      pods = "10"
    }
    business = {
      cpu = "8"
      memory = "16Gi"
      pods = "30"
    }
    enterprise = {
      cpu = "32"
      memory = "64Gi"
      pods = "100"
    }
  }
}

resource "kubernetes_namespace" "tenant" {
  metadata {
    name = "tenant-${var.tenant_id}"
    labels = {
      "tenant-id" = var.tenant_id
      "tenant-plan" = var.tenant_plan
    }
  }
}

resource "kubernetes_resource_quota" "tenant" {
  metadata {
    name = "quota"
    namespace = kubernetes_namespace.tenant.metadata[0].name
  }
  spec {
    hard = {
      "requests.cpu" = local.quota[var.tenant_plan].cpu
      "requests.memory" = local.quota[var.tenant_plan].memory
      "count/pods" = local.quota[var.tenant_plan].pods
    }
  }
}

resource "helm_release" "app" {
  name = "app"
  namespace = kubernetes_namespace.tenant.metadata[0].name
  chart = "../charts/saas-app"
  version = var.app_version
  set {
    name = "tenantId"
    value = var.tenant_id
  }
  set {
    name = "database.host"
    value = var.db_host
  }
  set_sensitive {
    name = "database.password"
    value = random_password.db_pass.result
  }
}

terraform workspace new tenant-acme && terraform apply -var="tenant_id=acme" -var="tenant_plan=business" – a new tenant ready in 5–7 minutes.

Database: isolation and migrations

For Bridge model (shared K8s, separate DB), each tenant gets a separate PostgreSQL database in a shared cluster or a separate RDS instance for enterprise. Migrations via Flyway or Liquibase run in Init Container on deploy:

initContainers:
  - name: db-migrate
    image: company/flyway:9
    env:
      - name: FLYWAY_URL
        value: "jdbc:postgresql://{{ .Values.database.host }}/tenant_{{ .Values.tenantId }}"
      - name: FLYWAY_PASSWORD
        valueFrom:
          secretKeyRef:
            name: tenant-db-secret
            key: password
    command: ["flyway", "migrate"]

With 500 tenants, this is 500 parallel Init Containers – we control via maxSurge and deploy time.

Observability: splitting metrics by tenant

Prometheus with label tenant_id in metrics allows per-tenant dashboards and alerts. Grafana with variable $tenant is standard. All application logs must contain tenant_id, otherwise isolating a problem during an incident is impossible. For Loki structured logging:

---
# promtail pipeline stage
pipeline_stages:
  - json:
      expressions:
        tenant_id: tenant_id
        level: level
  - labels:
      tenant_id:
      level:

Queries for a specific tenant's logs: {namespace="tenant-acme"} | json | level="error".

How to ensure data residency and compliance?

For clients requiring data storage in a specific region (GDPR, PDPL, Russian federal laws), a Silo model with a dedicated K8s cluster in the required region or at least a separate RDS instance in the required AZ/region is needed. This requirement should be addressed at the architecture stage – adding it later to a Pool model is extremely painful. In our practice, we have repeatedly encountered projects where compliance requirements emerged after launch, and refactoring cost 2–3 times more.

What is included in the work

  • Architecture audit – analysis of current isolation, compliance, and scaling requirements
  • Model design – choosing Silo/Pool/Bridge with justification
  • Terraform tenant module – automatic creation of namespace, quotas, network policies, database
  • Helm chart for multi-tenant deploy – templating application for tenant_id
  • CI/CD onboarding pipeline – full automation from form to DNS
  • Observability – per-tenant metrics, logs, traces
  • Load testing – isolation check (noisy neighbor)
  • Documentation – runbook for adding a new tenant, troubleshooting
  • Team training – 1–2 sessions on operating the multi-tenant environment
  • Support – 30 days post-implementation support

Deliverables

Upon completion, you receive: documentation with runbooks, access to infrastructure code, training sessions, and ongoing support for 30 days.

Timelines and cost

Stage Duration
Requirements audit, isolation model selection 3–5 days
Development of Terraform tenant module 1–2 weeks
Helm chart for multi-tenant deploy 1 week
CI/CD onboarding (full automation) 1 week
Observability (metrics, logs per-tenant) 3–5 days
Load testing (isolation, noisy neighbor) 3–5 days

Total for a typical SaaS from scratch: 6–10 weeks. For refactoring an existing single-tenant architecture – estimated separately after codebase and infrastructure audit. Cost is calculated individually for your project. Get a consultation – our engineers with experience implementing multi-tenant solutions in high-load SaaS will find the optimal solution. Contact us to discuss your case.

Learn more about multitenancy on Wikipedia.