Production-ready Helm chart for Kubernetes multi-tenancy — automate namespace provisioning, RBAC, resource quotas, network policies, and LimitRanges for every tenant in a single declarative
values.yaml.
Manually managing dozens (or hundreds) of tenant namespaces is error-prone and doesn't scale. k8s-multitenant gives platform teams a single Helm chart to declaratively enforce:
- Namespace isolation per tenant
- Fine-grained RBAC — roles bound to team identities
- ResourceQuota + LimitRange — CPU/memory guardrails per tier
- Network isolation — default-deny ingress/egress policies
- Consistent governance — every tenant gets the same baseline, every time
| Feature | Description |
|---|---|
| Namespace automation | Create and annotate one or many namespaces per tenant |
| RBAC | ClusterRole / Role + RoleBindings scoped to tenant namespaces |
| Resource quotas | Per-tenant CPU, memory, and object-count limits |
| LimitRanges | Default container requests/limits injected at admission |
| Network policies | Tenant-to-tenant isolation with allow-lists |
| Labels & annotations | Standardized metadata for cost allocation and tooling |
| Multi-tier support | small, medium, large, custom resource profiles |
| GitOps-ready | Pure declarative — works with Argo CD, Flux, or plain helm upgrade |
┌─────────────────────────────────────────────────────────┐
│ Platform Team │
│ helm install / helm upgrade │
└───────────────────────┬─────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ k8s-multitenant Helm Chart │
│ │
│ ┌─────────────┐ ┌──────────┐ ┌────────────────────┐ │
│ │ Namespace │ │ RBAC │ │ Resource Quotas │ │
│ │ Template │ │ Template │ │ + LimitRanges │ │
│ └──────┬──────┘ └────┬─────┘ └─────────┬──────────┘ │
│ │ │ │ │
│ ┌──────▼──────────────▼──────────────────▼───────────┐ │
│ │ Network Policy Templates │ │
│ └────────────────────────────────────────────────────┘ │
└──────────────────────────┬──────────────────────────────┘
│ creates
┌────────────────┼────────────────┐
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ ns/team-a │ │ ns/team-b │ │ ns/team-c │
│ quota │ │ quota │ │ quota │
│ rbac │ │ rbac │ │ rbac │
│ netpol │ │ netpol │ │ netpol │
└─────────────┘ └─────────────┘ └─────────────┘
| Tool | Minimum Version |
|---|---|
| Kubernetes | 1.24+ |
| Helm | 3.10+ |
| kubectl | 1.24+ |
The chart is distributed as an OCI artifact on the GitHub Container Registry. Helm 3.8+ supports OCI natively — no helm repo add required.
helm install my-tenants oci://ghcr.io/clouddrove/k8s-multitenant \
--version 1.0.0 \
--namespace platform-system \
--create-namespacehelm install my-tenants oci://ghcr.io/clouddrove/k8s-multitenant \
--version 1.0.0 \
--namespace platform-system \
--create-namespace \
-f my-tenants-values.yamlhelm upgrade my-tenants oci://ghcr.io/clouddrove/k8s-multitenant \
--version 1.0.0 \
-f my-tenants-values.yamlhelm show values oci://ghcr.io/clouddrove/k8s-multitenant --version 1.0.0Create a values.yaml to onboard your first tenant:
tools:
create: true
namespace: k8s-tools
tenants:
- name: team-alpha
labels:
cost-center: "eng-platform"
environment: "production"
rbac:
subjects:
- kind: Group
name: team-alpha-admins
apiGroup: rbac.authorization.k8s.io
rbac:
create: true
serviceAccountName: default
resourceQuota:
enabled: true
default:
requests.cpu: "2"
requests.memory: 4Gi
limits.cpu: "4"
limits.memory: 8Gi
pods: "20"
services: "5"
networkPolicy:
enabled: true
vpcCidr: "10.0.0.0/8" # set to your VPC CIDR
allowInternetEgress: false # set true if pods need external HTTP/HTTPSApply it:
helm install tenants oci://ghcr.io/clouddrove/k8s-multitenant \
--version 1.0.0 \
-f values.yaml \
-n platform-system --create-namespace| Parameter | Description | Default |
|---|---|---|
global.labels |
Labels applied to all managed resources | {} |
global.annotations |
Annotations applied to all managed resources | {} |
| Parameter | Description | Default |
|---|---|---|
tools.create |
Create a shared tools namespace | true |
tools.namespace |
Name of the tools namespace | "k8s-tools" |
tools.labels |
Additional labels | {} |
tools.annotations |
Additional annotations | {} |
| Parameter | Description | Default |
|---|---|---|
tenants[].name |
Tenant identifier — becomes the namespace name | required |
tenants[].labels |
Additional labels on the namespace | {} |
tenants[].annotations |
Additional annotations on the namespace | {} |
tenants[].resourceQuota |
Per-tenant quota spec (overrides resourceQuota.default) |
{} |
tenants[].limitRange |
Per-tenant LimitRange spec (overrides limitRange.default) |
{} |
| Parameter | Description | Default |
|---|---|---|
rbac.create |
Create Role + RoleBinding for each tenant namespace | true |
rbac.serviceAccountName |
ServiceAccount to bind to the tenant role | "default" |
rbac.defaultRules |
RBAC rules granted in each tenant namespace | see values.yaml |
| Parameter | Description | Default |
|---|---|---|
resourceQuota.enabled |
Create a ResourceQuota in each tenant namespace | true |
resourceQuota.default |
Default hard quota applied to all tenants | see values.yaml |
| Parameter | Description | Default |
|---|---|---|
limitRange.enabled |
Create a LimitRange in each tenant namespace | true |
limitRange.default |
Default container limits applied to all tenants | see values.yaml |
| Parameter | Description | Default |
|---|---|---|
networkPolicy.enabled |
Global default; create NetworkPolicy in every tenant namespace | true |
networkPolicy.vpcCidr |
VPC CIDR used for ALB ingress + internal service egress rules | "10.0.0.0/8" |
networkPolicy.allowInternetEgress |
Allow outbound HTTPS/HTTP to the public internet | false |
tenants[].networkPolicy.enabled |
Per-tenant override for networkPolicy.enabled |
inherits global |
See the examples/ directory for ready-to-use configurations:
| Example | Description |
|---|---|
basic-tenant/ |
Single tenant with default settings |
advanced-tenant/ |
Multi-team setup with custom quotas and network policies |
production-tenant/ |
Production-grade config with strict isolation and cost labels |
eks/ |
EKS-specific — IAM roles via aws-auth groups, IRSA annotations |
aks/ |
AKS-specific — AAD Group Object IDs as RBAC subjects |
gke/ |
GKE-specific — Google Groups, Workload Identity annotations |
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: platform-tenants
namespace: argocd
spec:
project: default
source:
repoURL: ghcr.io/clouddrove
chart: k8s-multitenant
targetRevision: 1.0.0
helm:
valueFiles:
- values.yaml
destination:
server: https://kubernetes.default.svc
namespace: platform-system
syncPolicy:
automated:
prune: true
selfHeal: trueapiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: k8s-multitenant
namespace: flux-system
spec:
interval: 1h
type: oci
url: oci://ghcr.io/clouddrove
---
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
name: platform-tenants
namespace: platform-system
spec:
chart:
spec:
chart: k8s-multitenant
version: ">=1.0.0"
sourceRef:
kind: HelmRepository
name: k8s-multitenant
namespace: flux-system
values: {}- All namespaces are created with a default-deny network policy unless explicitly overridden.
- RBAC bindings are namespace-scoped — no cluster-wide privilege escalation.
- LimitRanges enforce default CPU/memory limits so unbounded containers can't consume all cluster resources.
- Resource quotas prevent noisy-neighbour scenarios.
- See SECURITY.md for vulnerability reporting.
Contributions are welcome! Please read CONTRIBUTING.md before opening a pull request.
# Clone the repo
git clone https://github.com/clouddrove/k8s-multitenant.git
cd k8s-multitenant
# Lint the chart
helm lint charts/k8s-multitenant
# Run template tests
helm template test-release charts/k8s-multitenant -f examples/basic-tenant/values.yamlSee CHANGELOG.md for release history.
This project is licensed under the MIT License.
- kiosk — multi-tenancy extension for Kubernetes
- HNC (Hierarchical Namespace Controller) — namespace hierarchy for Kubernetes
- Capsule — Kubernetes multi-tenancy operator
- vcluster — virtual clusters for stronger isolation
Made with ❤️ by CloudDrove