Flux CD Flux CD

Configure Multi-Cluster with Flux

Manage several clusters from one repository, with shared infrastructure and per-cluster differences.

12 min Intermediate

Flux scales to many clusters by running one set of controllers in each cluster, all following the same Git repository at different paths. There is no central hub to register clusters with. This guide shows how to lay out a single repository for staging and production, bootstrap each cluster, order infrastructure before applications, and when to use a remote-cluster setup instead.

One repository, one path per cluster

Flux follows a simple rule: every cluster is bootstrapped against its own folder under clusters/. A repository serving two clusters looks like this:

fleet-infra/
├── clusters/
│   ├── staging/
│   │   ├── flux-system/        # created by bootstrap
│   │   ├── infrastructure.yaml
│   │   └── apps.yaml
│   └── production/
│       ├── flux-system/
│       ├── infrastructure.yaml
│       └── apps.yaml
├── infrastructure/
│   ├── base/                   # shared: ingress, cert-manager, monitoring
│   ├── staging/
│   └── production/
└── apps/
    ├── base/
    ├── staging/
    └── production/

Shared definitions live in base/ folders, and each environment folder holds the Kustomize overlay for what differs (see the overlays guide).

Bootstrap each cluster

Switch your kubectl context to each cluster in turn and bootstrap against the same repository with a different path:

kubectl config use-context staging
flux bootstrap github --owner=<user> --repository=fleet-infra \
  --branch=main --path=clusters/staging --personal

kubectl config use-context production
flux bootstrap github --owner=<user> --repository=fleet-infra \
  --branch=main --path=clusters/production --personal

Each cluster now has its own Flux installation and its own flux-system folder in Git. Adding a third cluster later is one more bootstrap command with a new path.

Point each cluster at its own overlays

clusters/production/apps.yaml applies the production overlay:

apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: apps
  namespace: flux-system
spec:
  interval: 10m
  dependsOn:
    - name: infrastructure
  sourceRef:
    kind: GitRepository
    name: flux-system
  path: ./apps/production
  prune: true
  wait: true

clusters/staging/apps.yaml is identical except for path: ./apps/staging. The infrastructure Kustomization points at ./infrastructure/<env> in the same way, and has wait: true so applications only start once the ingress controller, certificate manager, and similar components are ready. The dependsOn field is what enforces that order.

Verify across clusters

Flux has no central dashboard, so check each cluster from its own context:

flux get kustomizations --context staging
flux get kustomizations --context production

Both should show Ready. Compare what Git says is deployed with what each cluster reports using flux tree and flux get all -A. For fleet-wide visibility, teams often collect Flux’s Prometheus metrics into Grafana.

Promote from staging to production

Because both clusters read the same repository, promotion is a commit. A common flow is:

  1. Change apps/staging and let Flux apply it to the staging cluster.
  2. Test and verify.
  3. Make the same change in apps/production in a reviewed pull request, or tag a release that the production GitRepository tracks (see the overlays guide).

Alternative: one control cluster, many remote clusters

Instead of running Flux in every cluster, you can run it in one place and have its Kustomizations and HelmReleases apply to other clusters. Set spec.kubeConfig on the resource to a Secret that holds the remote cluster’s kubeconfig:

spec:
  kubeConfig:
    secretRef:
      name: production-kubeconfig

This centralizes control, but it also puts credentials for every cluster in the control cluster. That differs from the usual Flux model, where credentials never leave each cluster and a compromised cluster can’t reach the others. Prefer a Flux installation per cluster unless you have a clear reason to centralize, such as lightweight edge clusters. If you do go this way, use cloud workload identity instead of long-lived kubeconfigs where your platform supports it, and look at the Flux Operator for managing fleets at scale.

Good habits

  • Keep each cluster’s flux-system folder out of shared overlays. Bootstrap owns it.
  • Use dependsOn so infrastructure, CRDs, and controllers install before the apps that need them.
  • Give each cluster its own SOPS key (see the secrets guide) so staging can’t decrypt production secrets.
  • Upgrade Flux one cluster at a time, starting with a non-production cluster, by re-running flux bootstrap with a newer CLI.

Popular Guides