In this episode you'll understand multi-tenancy with Flux: namespace, cluster, and repository-based tenancy models, using flux create tenant for per-tenant RBAC, resource quotas, and lockdown mode to strengthen isolation between tenants in one cluster.

In episode 11 you learned variable substitution, postBuild, and per-environment configuration strategies, including separating configuration between tenants. Now we go up a level: what if there's more than one team in a cluster, and each manages its own applications through Git?
This scenario is called multi-tenancy. In episode 12 we'll look at how FluxCD handles it: the tenancy model, the flux create tenant command, the tenant structure in Git and in the cluster, access control, and lockdown mode.
Multi-tenancy is the practice of sharing one infrastructure, usually one Kubernetes cluster, with many teams or tenants, without letting one tenant interfere with or access another tenant's resources. In the GitOps context, each tenant typically has:
FluxCD is designed to support multi-tenancy natively. A Kustomization can be run with the tenant's service account, so the changes synced from Git only have power within the tenant's domain.
| Model | Description | Isolation | Suitable For |
|---|---|---|---|
| Namespace-based | Many tenants share one cluster, each in its own namespace | Medium | Internal teams, dev and staging environments |
| Cluster-based | One full cluster per tenant | Strong | Production, strict regulation, external customers |
| Repository-based | Tenants share a cluster, each has its own Git repository | Medium | Teams with separate Git processes |
| Hybrid | A combination, e.g. prod per cluster, dev shared | Custom | Large organizations |
Note
The main focus of this series is namespace-based tenancy because it's the most common way to use FluxCD and the cheapest to operate. The other models are just namespace-based tenancy strengthened with additional isolation layers.
The fastest way to create a correct tenant is the flux create tenant command, which produces RBAC objects consistent with the official FluxCD recommendations.
flux create tenant dev-team \
--with-namespace=dev-team \
--labels=team=dev \
--export > dev-team-rbac.yamlThe command above produces a dev-team namespace, a flux ServiceAccount, a dev-team:flux Role limited to that namespace, and a RoleBinding that binds it. Note that the result is a Role and RoleBinding, not a ClusterRole and ClusterRoleBinding — basic tenant isolation is limited to its own namespace.
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: dev-team:flux
namespace: dev-team
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: dev-team:flux
subjects:
- kind: ServiceAccount
name: flux
namespace: dev-teamApply it to the cluster, then review the result before using it in production:
kubectl apply -f dev-team-rbac.yamlImportant
Never apply the output of flux create tenant without reviewing its contents first. Understand every object it produces before applying it to a production cluster.
A healthy tenant has a clear structure, both in Git and in the cluster.
The namespace is the most basic isolation boundary in Kubernetes. Each tenant gets its own namespace, and all its application resources live there. With the RBAC from flux create tenant, a tenant's service account can't touch other namespaces.
Each tenant references its own repository through a GitRepository, with separate credentials in its own Secret.
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: dev-team-apps
namespace: dev-team
spec:
interval: 5m
url: https://github.com/acme/dev-team-apps
ref:
branch: mainThe serviceAccountName field is the key to this isolation — without it, the Kustomization runs with the default service account used by flux-system.
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: dev-team-apps
namespace: dev-team
spec:
serviceAccountName: flux
sourceRef:
kind: GitRepository
name: dev-team-apps
path: ./apps
prune: true
interval: 5mNamespace-based tenancy without a ResourceQuota lets one tenant monopolize cluster resources. Always install a quota so each tenant's resource usage can be measured and limited:
apiVersion: v1
kind: ResourceQuota
metadata:
name: dev-team-quota
namespace: dev-team
spec:
hard:
requests.cpu: "4"
requests.memory: 8Gi
limits.cpu: "8"
limits.memory: 16Gi
pods: "20"
services: "10"| Layer | Mechanism | Function |
|---|---|---|
| Cluster | Kubernetes RBAC | Limits what a tenant's service account can do |
| Git | Repository permissions, e.g. GitHub teams | Limits who can change tenant manifests |
| Namespace | Role, NetworkPolicy, and Quota | Limits the tenant's scope of work in the cluster |
For Git access, each tenant's credentials must be separate. Don't share a Personal Access Token between tenants — use a deploy key or bot account per repository. On the cluster side, make sure tenant service accounts don't have impersonate, escalate, or access to ClusterRoles.
Lockdown mode is a collection of practices to tighten a multi-tenant environment. At bootstrap, FluxCD provides two important flags:
flux bootstrap github \
--owner=acme \
--repository=fleet \
--branch=main \
--path=./clusters/prod \
--network-policy=true \
--no-cross-namespace-refs=true| Flag | Effect |
|---|---|
--network-policy=true | Creates a deny-all NetworkPolicy in the flux-system namespace |
--no-cross-namespace-refs=true | Forbids cross-namespace references, e.g. a Kustomization in one namespace referencing a GitRepository in another |
Tenant service accounts must not have permission to modify Roles, RoleBindings, or ClusterRoles. The escalate and bind permissions must be removed from tenant rules, because without this a tenant could grant itself additional permissions.
The impersonate permission allows an identity to imitate another identity. Tenants must not have it, because it could be used to bypass the RBAC boundaries that are already installed.
Besides the deny-all in flux-system, install per-tenant NetworkPolicies to cut communication between tenants:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all
namespace: dev-team
spec:
podSelector: {}
policyTypes:
- Ingress
- EgressAdmission controllers like OPA Gatekeeper or Kyverno enforce policy at the cluster level: rejecting pods that escalate privileges, forbidding hostPath, forcing every tenant namespace to have a quota, and more. This is the final layer that closes the gaps that slip through RBAC and NetworkPolicy.
Warning
Lockdown mode is not a single command, but a combination of minimal RBAC, NetworkPolicy, and admission policy. Start from flux create tenant, then tighten up gradually.
In this episode you learned how to isolate many teams in one cluster using FluxCD.
The key takeaways:
In the next episode, episode 13, we'll discuss Image Automation — how FluxCD monitors the container registry, evaluates new image versions, and automatically updates Git. See you in the next episode!