flux2-multi-tenancy: giving each tenant a namespace and a service account
Manage multi-tenant clusters with Flux
At a glance
- What is it?
- This repository is the reference layout for running Flux v2 across a fleet of shared Kubernetes clusters, where isolation is enforced by namespaces, RBAC, admission policies and a multi-tenancy lockdown that makes the controller impersonate each tenant's service account. It ships a fleet repository, tenant repository templates and the admission policies that back the model.
- Who is it for?
- flux2-multi-tenancy suits a platform team that runs GitOps across many tenants on shared clusters and needs the isolation story to be enforced by the cluster rather than by convention, since the admission policies and the lockdown are the substance of the repository rather than the directory layout. It does not suit a single-team cluster, where the whole namespace and service account arrangement is overhead.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 168 days ago.
- What is it written in?
- GitHub does not report a main language for this repository.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The trust boundary is a namespace, and sharing one is not supported
The repository is a starting point for managing multi-tenant clusters with Git and Flux v2, and the isolation model is stated in one sentence that is worth reading carefully before anything else.
The trust boundary of a tenant is the Kubernetes namespace assigned to it by the platform admin. Sharing namespaces between tenants is not supported, as it would break the isolation guarantees of Flux.
That is a hard constraint rather than a recommendation, and it has a consequence for how you plan the tenancy model. If your platform's natural unit is a team that wants several applications in one namespace, Flux's model does not accommodate that, and the choice is between one namespace per tenant and a different tool.
Flux has built-in support for multi-tenancy on shared clusters by leveraging Kubernetes namespaces and RBAC to isolate tenants and their workloads. A single Flux instance can manage multiple tenants by reconciling Kubernetes manifests from different Git repositories and applying them with the permissions of a service account assigned to each tenant. That last phrase is the mechanism, and the lockdown section below is what makes it true.
The two roles are defined in the README with a clean division. The platform admin has cluster admin access to the fleet of clusters, maintainer access to the fleet Git repository, manages cluster-wide resources such as CRDs, controllers and cluster roles, manages tenants by assigning namespaces, service accounts and RBAC, and onboards the tenant's main repository using Flux GitRepository and Kustomization custom resources.
The tenant has no direct access to the Kubernetes API at all, since all changes must be made via Git. The tenant has maintainer access to the GitOps repository and the apps repositories, can deploy apps with Flux Kustomizations and HelmReleases from approved sources, which are GitRepository, OCIRepository and HelmRepository, can automate image updates with Flux ImagePolicy and ImageUpdateAutomation from approved ImageRepository sources, and can only deploy workloads in the namespaces the platform admin assigned.
What the multi-tenancy lockdown actually denies
The lockdown is the part that turns a namespace convention into an enforced boundary, and it does three things.
First, it denies cross-namespace access to custom resources, ensuring that a tenant cannot use another tenant's sources or subscribe to their events. Without this, a tenant who can read Flux custom resources in other namespaces could point a Kustomization at a GitRepository belonging to someone else, and the whole model of separate repositories per tenant would collapse.
Second, it denies accesses to Kustomize remote bases, ensuring all resources refer to local files. The stated meaning is that only approved Flux Sources can affect the cluster state. This closes the path where a Kustomization pulls a base from a URL the tenant controls, since a remote base is arbitrary content that never passes through the admission policy of a Flux source.
Third, it impersonates tenant service accounts instead of using the controller's service account to reconcile. The meaning given is that if a tenant does not specify a service account in a Flux Kustomization or HelmRelease, it automatically defaults to the flux account in the tenant's namespace.
That third item is the one that changes day to day behaviour most. Without it, the controller reconciles everything as itself, and the controller's permissions are cluster wide, so a tenant who can author a manifest can author anything the controller can do. With impersonation, the effective permissions of any reconcile are the permissions of the tenant's own service account in that namespace, so a tenant cannot exceed the RBAC the platform admin granted even if they write a manifest that asks for more.
The bootstrap section repeats the ordering requirement: when bootstrapping a cluster, platform admins must set up the Flux instance with the multi-tenancy lockdown and configure the reconciliation of the admission policies before the tenant repositories.
The fleet repository, and why policies reconcile before tenants
The fleet repository has three top level directories, and the layout is a diagram of the operational model.
The clusters directory contains the Flux configuration per cluster. The policies directory contains the cluster-wide admission policies, which are CEL ValidatingAdmissionPolicy resources. The tenants directory contains namespaces, RBAC and the Flux custom resources for registering tenant repositories.
The example tree shows two clusters, production and staging, and three policy and tenant variants each:
βββ clusters
βΒ Β βββ production
βΒ Β βββ staging
βββ policies
βΒ Β βββ base
βΒ Β βββ production
βΒ Β βββ staging
βββ tenants
βββ base
βββ production
βββ stagingThe ordering is expressed inside the clusters directory, where policies.yaml and tenants.yaml define the reconciliation sequence for each cluster. The policies Kustomization points at the policy path with prune and wait enabled and an hourly interval:
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: policies
namespace: flux-system
spec:
serviceAccountName: kustomize-controller
sourceRef:
kind: GitRepository
name: flux-system
path: ./policies/staging
prune: true
wait: true
interval: 1h
timeout: 2mAnd the tenants Kustomization declares a dependency on it:
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: tenants
namespace: flux-system
spec:
serviceAccountName: kustomize-controller
dependsOn:
- name: policies
sourceRef:
kind: GitRepository
name: flux-system
path: ./tenants/staging
prune: true
interval: 1h
timeout: 5mWith dependsOn, Flux applies the policies before reconciling the tenants, and the stated reason is that this ensures the resources from tenant repositories are subject to the admission policies. The wait and timeout differences between the two are also meaningful: the policy Kustomization waits for its resources to be ready within two minutes, while the tenant one has a five minute timeout and no wait, because tenant workloads take longer to become healthy and should not block the next policy application.
Tenant repositories get a base and two overlays, and only the base is theirs
A tenant repository is structured so the platform controls the shape and the tenant controls the content, within limits.
The base directory contains the HelmRelease manifests and the Flux sources for the tenant's application delivery. The staging directory contains Kustomize patches for deploying pre-releases on the staging cluster, and production contains Kustomize patches for deploying stable releases on the production cluster. The example layout shows a base with a kustomization.yaml, a podinfo-release.yaml and a podinfo-repository.yaml, and the two overlays each with a kustomization.yaml and a podinfo-values.yaml.
The structure mirrors the cluster fleet, with a base directory for the common configuration and overlays for staging and production environments. That symmetry is what makes a promotion from staging to production a change of overlay rather than a rewrite.
The statement about control is important and it is bounded. The tenants have full control over their app delivery, adding or modifying Helm releases as needed within the constraints defined by the platform admins, with the examples given being allowed namespaces and Helm chart repositories.
So the division of authority is: the platform admin decides which namespaces, which chart repositories and which sources are legitimate, and the tenant decides what to deploy within that envelope. The admission policies are where the first half is actually enforced, which is why the policies directory is a first class part of the repository rather than an appendix.
Two bootstrap routes, and where the state of the project stands
There are two documented ways to bootstrap a multi-tenant cluster.
The first is the flux bootstrap command with a kustomize patch configured for multi-tenancy lockdown. The repository contains that patch for the CLI approach in the clusters/staging/flux-system/kustomization.yaml and clusters/production/flux-system/kustomization.yaml files. The example for the staging cluster looks like this:
flux bootstrap github \
--owner=<org> \
--repository=<repository> \
--branch=main \
--path=clusters/stagingThe path argument points at the per cluster directory in the fleet repository, and the branch is the one to reconcile from. The Flux CLI supports SSH and token-based authentication, though the sentence describing that is cut off in the source available.
The second route is the Flux Operator with a FluxInstance manifest configured with multitenant: true, which is a declarative way to install the same instance.
Both routes carry the same obligation. The lockdown has to be in place and the policies have to be reconciled before any tenant repository, because the whole enforcement argument depends on the policies existing first and the dependsOn relationship making that order hold.
The repository layout is small. The top level holds clusters, policies, tenants, docs, a .fluxschema.yml, a .sourceignore, a .gitignore and a .github directory, with an Apache-2.0 LICENSE. The primary language is not recorded, which is consistent with a repository that is mostly YAML.
On maintenance, the last push was on 2026-05-08 and the repository is not archived. The only tagged release is v1.0.0 from 2023-10-10, and the release note text for it is empty in the source available, so there is no changelog to work from. The practical reading is that this is a reference layout that the Flux project points at from its own documentation, with the policies being the part most likely to need attention as Kubernetes admission policy features evolve.
Editorial conclusion
flux2-multi-tenancy suits a platform team that runs GitOps across many tenants on shared clusters and needs the isolation story to be enforced by the cluster rather than by convention, since the admission policies and the lockdown are the substance of the repository rather than the directory layout. It does not suit a single-team cluster, where the whole namespace and service account arrangement is overhead. Before you adopt it, read the admission policies under policies/ to see exactly what is denied, confirm the multi-tenancy lockdown is enabled on every cluster you bootstrap, and remember that the last push was on 2026-05-08 and the only tagged release is v1.0.0 from 2023-10-10, so the policies are the part to keep current yourself.
Frequently asked questions
What is the trust boundary for a tenant in flux2-multi-tenancy?
The Kubernetes namespace assigned to the tenant by the platform admin. Sharing namespaces between tenants is not supported, because it would break the isolation guarantees of Flux.
What does the Flux multi-tenancy lockdown do?
It denies cross-namespace access to custom resources so a tenant cannot use another tenant's sources or subscribe to their events, denies accesses to Kustomize remote bases so all resources refer to local files, and impersonates tenant service accounts instead of using the controller's service account to reconcile, defaulting to the flux account in the tenant's namespace when none is specified.
Why do the admission policies reconcile before the tenant resources?
The tenants Kustomization declares dependsOn the policies Kustomization, so Flux applies the policies first. That ensures the resources from tenant repositories are subject to the admission policies rather than being reconciled before the guardrails exist.
How do I bootstrap a multi-tenant cluster with flux2-multi-tenancy?
Either use the flux bootstrap command with the multi-tenancy kustomize patch included in the clusters/staging/flux-system and clusters/production/flux-system directories, pointing the path at the right cluster directory, or use the Flux Operator with a FluxInstance manifest configured with multitenant: true.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/fluxcd-flux2-multi-tenancy)Was this useful?