Flux is a GitOps tool that uses a reconciliation loop to keep your Kubernetes cluster in sync with a desired state defined in Git. When you’re managing multiple teams or applications within a single Kubernetes cluster, you’ll often find yourself needing to share a single Flux source (like a Git repository or an S3 bucket) across different namespaces. This is where the Source controller’s Kustomization and HelmRelease resources come into play, but doing it "safely" means understanding a few key concepts.
Let’s see how this looks in practice. Imagine we have a central Git repository containing common application configurations. We want to deploy an application in the dev namespace and another in the staging namespace, both pulling from this same repository.
Here’s a GitRepository source defined in the flux-system namespace, which is where the Flux controllers typically run:
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: GitRepository
metadata:
name: common-apps-repo
namespace: flux-system
spec:
interval: 1m
url: ssh://git@github.com/my-org/common-apps.git
ref:
branch: main
secretRef:
name: github-ssh-key
Now, we want to use this common-apps-repo to deploy an application in the dev namespace. We’ll create a Kustomization resource in the dev namespace that references the GitRepository in flux-system:
---
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
name: my-app-dev
namespace: dev # The target namespace for the application
spec:
interval: 5m
path: ./apps/my-app/dev # Path within the Git repo
sourceRef:
kind: GitRepository
name: common-apps-repo # The name of the GitRepository
namespace: flux-system # The namespace where the GitRepository is defined
prune: true
wait: true
And similarly for the staging namespace:
---
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
name: my-app-staging
namespace: staging # The target namespace for the application
spec:
interval: 5m
path: ./apps/my-app/staging # Path within the Git repo
sourceRef:
kind: GitRepository
name: common-apps-repo # The name of the GitRepository
namespace: flux-system # The namespace where the GitRepository is defined
prune: true
wait: true
The core problem this solves is avoiding duplication. Instead of each namespace having its own GitRepository definition pointing to the same physical Git repo, you define the source once and reference it. This is crucial for maintainability. If the Git repository URL or credentials change, you only update one GitRepository object.
Internally, the Source controller (which runs in flux-system) is responsible for fetching from the GitRepository (or HelmRepository, Bucket, etc.). When a Kustomization or HelmRelease in a different namespace declares a sourceRef pointing to that GitRepository, the Source controller sees this dependency. It then makes the content of the fetched source available to the target controller (the Kustomize controller or Helm controller) that is responsible for the Kustomization or HelmRelease in the other namespace. This is typically done by creating a Source object (e.g., GitRepository, HelmRepository) in the target namespace that mirrors the one in flux-system and then the target controller uses this local mirror.
The key levers you control are:
interval: How often Flux checks the source for updates.urlandref: ForGitRepository, this defines what to fetch.secretRef: ForGitRepositoryandBucket, this points to the KubernetesSecretcontaining credentials.path: ForKustomization, this specifies the sub-directory within the repository to apply.namespaceinsourceRef: This is critical. It tells Flux where to find the shared source definition.
You might be tempted to put the GitRepository object in a namespace other than flux-system. While technically possible, it’s generally discouraged. The Flux controllers themselves are designed to run in a dedicated namespace (often flux-system), and having sources defined there centralizes the fetching logic and reduces the number of RBAC permissions needed across the cluster. You can, however, have Kustomization or HelmRelease objects in any namespace that can reference a source defined in flux-system.
The most surprising thing about this pattern is how granularly you can control access to specific parts of a shared repository. A single GitRepository can serve many Kustomization resources, each pointing to a different path within that repository. This allows for a highly organized monorepo structure where different teams or applications can consume only the configurations relevant to them, without needing separate repositories. The Kustomization controller, when processing a Kustomization resource, will only consider the specified path from the fetched source artifact, effectively isolating its view.
Once you’ve mastered sharing sources, the next logical step is managing dependencies between these cross-namespace Kustomizations.