← Back to Blog

Implementing GitOps with FluxCD for Multi-Tenant Kubernetes



Implementing GitOps with FluxCD for Multi-Tenant Kubernetes

Running a single Kubernetes cluster for one team is straightforward. Once you introduce multiple engineering teams requiring isolated environments, managing permissions and operational boundaries becomes complex.

Developers require autonomy to ship code via GitOps without accessing cluster-scoped resources or other teams' namespaces. FluxCD addresses this natively by pairing Kubernetes Role-Based Access Control (RBAC) with ServiceAccount impersonation.

Multi-tenancy in FluxCD relies on two levels of isolation: 1. Git repository isolation Each team manages its own repository with specific access permissions. 2. Kubernetes namespace isolation Reconcilers operate strictly within designated target namespaces.

Here is how to structure a multi-tenant FluxCD setup step by step.

---

1. Bootstrapping the Cluster Admin Repository

The cluster admin repository defines the overall cluster state, infrastructure components (like ingress controllers and monitoring tools), and tenant onboarding manifests. Only the platform engineering team should have write access to this repo.

# clusters/production/infrastructure.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
 name: infrastructure
 namespace: flux-system
spec:
 interval: 10m0s
 sourceRef:
 kind: GitRepository
 name: flux-system
 path: ./infrastructure
 prune: true
 wait: true

Key configurations: - kind: Kustomization: Uses Flux's Kustomize controller to sync manifests from Git. - namespace: flux-system: Core infrastructure remains managed centrally by the primary Flux instance. - path: ./infrastructure: Points to base cluster configs inside the admin repository. - prune: true: Deletes cluster resources automatically if they are removed from Git.

---

2. Defining Tenant Boundaries with RBAC

To onboard a tenant (for example, a frontend team), provision a dedicated namespace, a ServiceAccount for impersonation, and a Role / RoleBinding restricting that ServiceAccount to the tenant namespace.

# tenants/frontend/rbac.yaml
apiVersion: v1
kind: Namespace
metadata:
 name: frontend
---
apiVersion: v1
kind: ServiceAccount
metadata:
 name: frontend-reconciler
 namespace: frontend
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
 name: frontend-reconciler
 namespace: frontend
rules:
 - apiGroups: ["*"]
 resources: ["*"]
 verbs: ["*"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
 name: frontend-reconciler
 namespace: frontend
roleRef:
 apiGroup: rbac.authorization.k8s.io
 kind: Role
 name: frontend-reconciler
subjects:
 - kind: ServiceAccount
 name: frontend-reconciler
 namespace: frontend

Key configurations: - Namespace: Defines the logical isolation boundary for the team. - ServiceAccount: The identity Flux impersonates when applying team manifests. - Role & RoleBinding: Grants frontend-reconciler full permissions within the frontend namespace only. If a team attempts to deploy a cluster-scoped object (such as a ClusterRole), Flux rejects the deployment due to RBAC restrictions.

---

3. Wiring the Tenant's Git Repository

Next, configure Flux to monitor the tenant's Git repository while impersonating their restricted ServiceAccount.

# tenants/frontend/sync.yaml
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: GitRepository
metadata:
 name: frontend-repo
 namespace: frontend
spec:
 interval: 5m0s
 url: ssh://git@github.com/my-org/frontend-apps.git
 ref:
 branch: main
 secretRef:
 name: frontend-git-auth # Deploy key secret
---
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
 name: frontend-apps
 namespace: frontend
spec:
 interval: 10m0s
 sourceRef:
 kind: GitRepository
 name: frontend-repo
 path: ./deploy/production
 prune: true
 serviceAccountName: frontend-reconciler

Key configurations: - GitRepository: Created inside the tenant namespace (frontend) to keep deployment keys (secretRef) isolated from other teams. - serviceAccountName: Directs the global Flux Kustomize controller to relinquish cluster-admin privileges and impersonate frontend-reconciler during reconciliation. - targetNamespace (optional setting or implicit scope): Scopes operations to frontend, preventing accidental or unauthorized cross-namespace deployments.

---

Summary

Combining repository-level access control, namespace boundaries, and Flux's ServiceAccount impersonation provides engineering teams with self-service deployments while maintaining cluster infrastructure security.