MATIH Platform is in active MVP development. Documentation reflects current implementation status.
17. Kubernetes & Helm
Namespaces
Tenant Namespaces

Tenant Namespaces

MATIH provisions isolated Kubernetes namespaces for each tenant, providing resource isolation, network boundaries, and independent scaling. Tenant namespaces are created automatically during the tenant provisioning lifecycle (see Chapter 7).


Namespace Naming Convention

Tenant namespaces follow the pattern: tenant-{slug}

Examples:

  • tenant-acme for Acme Corporation
  • tenant-bigcorp for BigCorp Inc.
  • tenant-startup-xyz for Startup XYZ

Provisioning Flow

When a new tenant is created through the Tenant Service API, the following Kubernetes resources are provisioned:

  1. Namespace with labels for identification and network policy matching
  2. ResourceQuota based on the tenant's subscription tier
  3. LimitRange with default container resource limits
  4. NetworkPolicy for tenant isolation
  5. ServiceAccount with minimal RBAC permissions
  6. Image pull secrets synced from the platform ACR
  7. ConfigMap with tenant-specific configuration
  8. Secrets synced via External Secrets Operator from the tenant's vault

Resource Quotas by Tier

TierCPU RequestsMemory RequestsPodsStorage
STARTER816Gi3050Gi
STANDARD3264Gi100200Gi
ENTERPRISE80160Gi300500Gi
UNLIMITEDNo limitNo limit5002Ti

Network Isolation

Each tenant namespace has a default-deny network policy with explicit allowances:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: tenant-default-deny
  namespace: tenant-acme
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress
 
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: tenant-allow-platform
  namespace: tenant-acme
spec:
  podSelector: {}
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              matih.io/namespace-type: platform
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              matih.io/namespace-type: platform
      ports:
        - protocol: TCP
          port: 8080
        - protocol: TCP
          port: 5432
        - protocol: TCP
          port: 6379
    - to:
        - namespaceSelector: {}
      ports:
        - protocol: UDP
          port: 53

Tenants are fully isolated from each other -- no cross-tenant traffic is allowed.


Dedicated Ingress

Production tenants receive a dedicated NGINX Ingress Controller with a separate LoadBalancer IP:

# Deployed via infrastructure/helm/ingress-nginx/values-tenant.yaml
controller:
  ingressClassResource:
    name: "nginx-tenant-acme"
  service:
    type: LoadBalancer

The tenant's DNS zone is configured as a child of the platform zone:

matih.ai              (Platform zone)
  |
  +-- acme.matih.ai   (Tenant zone - NS delegation)
       |
       +-- api.acme.matih.ai    (A record -> Tenant LB IP)
       +-- app.acme.matih.ai    (A record -> Tenant LB IP)

Tenant Labels

Every resource in a tenant namespace carries billing and identification labels:

labels:
  matih.io/tenant-id: "tenant-acme"
  matih.io/tenant-tier: "ENTERPRISE"
  matih.io/cost-center: "CC-DATA-PLANE"
  matih.io/cost-type: "dynamic"
  matih.io/environment: "production"