Right, so you’ve got Flux humming along, deploying your apps beautifully. But now you’ve got a new problem: other people. Maybe it’s another team, a contractor, or a client who needs a sliver of your cluster. You need to give them a sandbox—a dedicated namespace with GitOps superpowers—without handing them the keys to the entire kingdom and your prized prod database. This, my friend, is where namespaced tenancy in Flux saves the day.

The core idea is brilliantly simple yet powerful: you can scope a Flux Kustomization or HelmRelease to reconcile resources only in a specific namespace. This means you can grant a tenant the ability to deploy their own apps via GitOps into their own namespace, while a central platform team retains control over everything else (like CRDs, cluster-level resources, and other namespaces). It’s the perfect balance of autonomy and control.

The Anatomy of a Namespaced Kustomization

Let’s be clear: a standard Kustomization is a cluster-scoped bully. It’ll deploy things wherever its YAML tells it to. A namespaced one is different. You create it inside the tenant’s namespace, and you set the spec.targetNamespace. This tells Flux, “Everything you find in my source, please reconcile it here, and nowhere else.”

Here’s what that looks like. First, create the tenant’s namespace itself. This is usually something you, the platform admin, would do.

# 1-namespace.yaml (Applied by a cluster-admin Kustomization)
apiVersion: v1
kind: Namespace
metadata:
  name: team-brontosaurus

Now, inside that namespace, you place the tenant’s Flux Kustomization. This is the magic piece.

# team-brontosaurus/flux-kustomization.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: team-brontosaurus-apps
  namespace: team-brontosaurus # <- CRITICAL: This object lives in the tenant's NS
spec:
  interval: 10m
  path: "./apps"
  prune: true
  sourceRef:
    kind: GitRepository
    name: team-brontosaurus-repo # This GitRepository must also exist in the same namespace
  targetNamespace: team-brontosaurus # <- The secret sauce! Forces all resources here.

Notice the two key fields: metadata.namespace and spec.targetNamespace. The first says where this custom resource lives, the second dictates where the resources it describes will be created. This is the linchpin of the entire setup.

Why This is a Security Masterstroke

This architecture is a gift for security and compliance. Here’s why:

  1. Role-Based Access Control (RBAC) is Simple: You can create a Role and RoleBinding in the team-brontosaurus namespace that grants the tenant’s identity (e.g., a CI/CD service account) just enough permission to create and update Kustomization objects only in that namespace. They can’t even see other namespaces, let alone modify them.

    # team-brontosaurus/rbac.yaml (Applied by platform team)
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      name: flux-controller
      namespace: team-brontosaurus
    rules:
    - apiGroups: ["kustomize.toolkit.fluxcd.io"]
      resources: ["kustomizations"]
      verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: brontosaurus-ci-bot
      namespace: team-brontosaurus
    subjects:
    - kind: ServiceAccount
      name: brontosaurus-ci-bot # Their CI service account
      namespace: team-brontosaurus
    roleRef:
      kind: Role
      name: flux-controller
      apiGroup: rbac.authorization.k8s.io
    
  2. Blast Radius Containment: If a tenant messes up their Kustomization and tries to deploy something malicious or just broken, the damage is confined to their own namespace. They can’t accidentally (or “accidentally”) delete a ClusterRole or disrupt another team’s work.

The Gotchas: Where This “Perfect” Plan Meets Reality

This isn’t all rainbows and unicorns. The designers made some choices that will bite you if you’re not aware. Here are the big ones:

  • The SourceRef Must Be Namespaced: This is the most common “aha!” moment. In the Kustomization example above, the sourceRef points to a GitRepository named team-brontosaurus-repo. The brutal, non-negotiable rule is that this GitRepository object must also be created in the same namespace as the Kustomization (team-brontosaurus). You cannot point a namespaced Kustomization to a cluster-scoped GitRepository source. This is a deliberate security feature, not an oversight. It means each tenant manages their own source definition.

  • Cluster-Scoped Resources are a No-Go: This is the big limitation. Your tenant cannot deploy anything that exists outside a namespace. This includes PersistentVolume, ClusterRole, ClusterRoleBinding, CustomResourceDefinition (CRD), and so on. If their application YAML includes one, Flux will reconcile it, the Kubernetes API will accept it, and then it will sit there, forever Ready=False with an error message about “namespaced scope.” The solution? The platform team must pre-install any required cluster-scoped resources (like CRDs) that the tenants need. It’s a trade-off: you lose tenant self-service for these items but gain immense control and stability.

  • Dependency Hell Across Namespaces: What if an app in team-brontosaurus needs to talk to a central, platform-managed Redis in the infra namespace? Your tenant’s Kustomization can’t create a Service in another namespace. The best practice is to use a pattern like Service Exports (from projects like KubeFed) or have the platform team create the necessary Service and Endpoints objects in the tenant’s namespace to point to the central service. It’s a bit more work, but it keeps the boundaries clean.

The bottom line? Multi-tenancy with namespaced Flux resources is the way to go for 90% of use cases where you need to isolate teams. It leverages native Kubernetes primitives (namespaces, RBAC) beautifully, is simple to understand, and is rock-solid stable. Just remember the rules: keep sources namespaced, pre-install CRDs, and design your inter-team dependencies carefully. Now go forth and delegate responsibly.