Architecture¶
Component breakdown of the GitOps Toolkit, reconciliation model, multi-tenancy, and deployment topology.
Component Overview¶
FluxCD v2 is built on the GitOps Toolkit (GOTK) -- a set of composable Kubernetes operators, each with its own CRDs, controller binary, and release cycle. Unlike ArgoCD's monolithic control plane, FluxCD deploys each controller as an independent Deployment in the flux-system namespace.
| Controller | CRDs Managed | Role |
|---|---|---|
source-controller |
GitRepository, OCIRepository, HelmRepository, HelmChart, Bucket |
Acquires artifacts from external sources (Git, OCI, Helm, S3); stores them as tarballs in-cluster; acts as the source-of-truth for all downstream controllers |
kustomize-controller |
Kustomization |
Fetches source artifacts, runs Kustomize builds, applies resulting manifests to the cluster via server-side apply |
helm-controller |
HelmRelease |
Reconciles Helm chart installations and upgrades; pulls charts from source-controller; runs helm upgrade with drift correction |
notification-controller |
Alert, Provider, Receiver |
Routes events from other controllers to external systems (Slack, Discord, Teams, GitHub commit status, webhooks) |
image-reflector-controller |
ImageRepository, ImagePolicy |
Scans container image registries; evaluates semantic version policies; exposes latest image tags for automation |
image-automation-controller |
ImageUpdateAutomation |
Automates Git commits to update image tags in source manifests based on ImagePolicy evaluations |
System Architecture¶
graph TB
subgraph Sources["External Sources"]
Git["Git Repository"]
OCI["OCI Registry"]
HelmRepo["Helm Repository"]
S3["S3 Bucket"]
end
subgraph Flux["Flux Control Plane (flux-system namespace)"]
SC["source-controller"]
KC["kustomize-controller"]
HC["helm-controller"]
NC["notification-controller"]
IRC["image-reflector-controller"]
IAC["image-automation-controller"]
end
subgraph K8s["Kubernetes API Server"]
API["API Server"]
end
subgraph Alerts["External Alerts"]
Slack["Slack"]
Teams["MS Teams"]
GHStatus["GitHub Commit Status"]
end
Git --> SC
OCI --> SC
HelmRepo --> SC
S3 --> SC
SC -->|"source artifacts (tarballs)"| KC
SC -->|"source artifacts (tarballs)"| HC
KC -->|"server-side apply"| API
HC -->|"helm upgrade"| API
KC -->|"events"| NC
HC -->|"events"| NC
SC -->|"events"| NC
IRC -->|"image tags"| IAC
IAC -->|"git commit"| Git
NC --> Slack
NC --> Teams
NC --> GHStatus
Reconciliation Model¶
Every Flux controller runs an independent reconciliation loop driven by the spec.interval field on its CRDs. There is no central scheduler.
sequenceDiagram
participant Git as Git Repository
participant SC as source-controller
participant KC as kustomize-controller
participant K8s as Kubernetes API
loop Every spec.interval (e.g. 5m)
SC->>Git: git fetch (or OCI pull, Helm index download)
SC->>SC: Detect revision change
alt New revision detected
SC->>SC: Build artifact tarball
SC->>K8s: Update GitRepository status (Ready, artifact URL)
end
end
loop Every spec.interval (e.g. 10m)
KC->>SC: Fetch artifact tarball
KC->>KC: Run kustomize build
KC->>K8s: server-side apply manifests
KC->>KC: Detect drift between desired and live
alt Drift detected and spec.prune=true
KC->>K8s: Delete stale resources
end
KC->>K8s: Update Kustomization status (Ready/Healthy)
end
Key reconciliation behaviors:
- Interval-based: Each resource defines its own spec.interval (minimum 1m, typically 5-10m for production)
- Event-driven acceleration: Webhook receivers (via notification-controller) can trigger immediate reconciliation instead of waiting for the interval
- Retry on failure: spec.retryInterval controls backoff when reconciliation fails (default: equal to spec.interval)
- Garbage collection: When spec.prune=true, the controller deletes resources that were previously applied but are no longer present in the source
Source Controller: Artifact Management¶
The source-controller is the foundation of the toolkit. It acts as an artifact proxy:
- Git repositories: Clones the repo at the specified ref (branch, tag, semver, commit SHA), applies
spec.ignorepath filtering, and packages the result as a tarball - OCI registries: Pulls OCI artifacts and exposes them as in-cluster artifacts
- Helm repositories: Downloads the Helm chart index and individual charts
- S3 buckets: Fetches objects from S3-compatible storage
Monorepo Performance
Using a monorepo with 10,000+ files can degrade source-controller performance. The controller must clone and package the entire repository even with path filtering. Use spec.ignore to exclude irrelevant paths, or use a separate deploy branch to keep the artifact small. For large monorepos, consider splitting into multiple GitRepository resources with narrow spec.ignore rules, or use OCI artifacts built in CI instead.
Source filtering example for monorepos:
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: monorepo
namespace: flux-system
spec:
interval: 1m
url: https://github.com/org/monorepo
ref:
branch: main
ignore: |
# exclude everything
/*
# include only the deploy directory
!/deploy/
Deployment Topology¶
Single-Cluster (Default)¶
All controllers run in the flux-system namespace. The flux bootstrap command installs the toolkit and creates a Kustomization resource that syncs the cluster state from a Git repository.
graph LR
Git["Git Repo"] --> SC["source-controller"]
SC --> KC["kustomize-controller"]
SC --> HC["helm-controller"]
KC --> K8s["Kubernetes API"]
HC --> K8s
Multi-Cluster (Hub and Spoke)¶
FluxCD supports multi-cluster deployments without a central management server. Two patterns exist:
- Hub and Spoke: One cluster runs Flux and reconciles resources on remote clusters using kubeconfig secrets
- Per-Cluster Bootstrap: Each cluster runs its own Flux instance, bootstrapped independently from the same or different Git repositories
graph TB
subgraph Hub["Hub Cluster"]
FluxHub["Flux (flux-system)"]
end
subgraph Spoke1["Spoke Cluster A"]
K8sA["Kubernetes API"]
end
subgraph Spoke2["Spoke Cluster B"]
K8sB["Kubernetes API"]
end
subgraph Git["Git Organization"]
RepoA["cluster-a manifest repo"]
RepoB["cluster-b manifest repo"]
end
FluxHub -->|"kubeconfig secret"| K8sA
FluxHub -->|"kubeconfig secret"| K8sB
RepoA --> FluxHub
RepoB --> FluxHub
Per-Cluster Bootstrap vs Hub-Spoke
The per-cluster bootstrap pattern is recommended for security isolation: each cluster's Flux instance only has credentials for its own Git repository. The hub-spoke pattern is simpler to manage but requires the hub cluster to hold kubeconfig secrets for all spoke clusters.
Multi-Tenancy¶
FluxCD implements multi-tenancy through Kubernetes-native mechanisms:
- Namespace isolation: Each tenant team deploys to their own namespace with their own
GitRepositoryandKustomizationCRDs - ServiceAccount impersonation: The
kustomize-controllerandhelm-controllerimpersonate the ServiceAccount specified in.spec.serviceAccountName, inheriting that account's RBAC permissions - Cross-namespace reference blocking: Set
--no-cross-namespace-refs=trueon all controllers to prevent tenants from referencing sources or secrets in other namespaces - Remote base blocking: Set
--no-remote-bases=trueonkustomize-controllerto prevent Kustomize remote bases from arbitrary URLs
# Kustomize patches for multi-tenancy lockdown
patches:
- patch: |
- op: add
path: /spec/template/spec/containers/0/args/-
value: --no-cross-namespace-refs=true
target:
kind: Deployment
name: "(kustomize-controller|helm-controller|notification-controller)"
- patch: |
- op: add
path: /spec/template/spec/containers/0/args/-
value: --default-service-account=default
target:
kind: Deployment
name: "(kustomize-controller|helm-controller)"
Storage Model¶
FluxCD is stateless by design. All persistent state lives in Kubernetes resources:
| Data | Storage |
|---|---|
| Source artifacts | PersistentVolumeClaim (source-controller artifact storage) |
| Reconciliation state | status subresource on each CRD (GitRepository, Kustomization, HelmRelease, etc.) |
| Git credentials | Secret resources in flux-system namespace |
| Tenant ServiceAccounts | ServiceAccount + RoleBinding per tenant namespace |
| Controller logs | Stdout (collected by cluster logging) |
Related Notes¶
- cicd/fluxcd/index
- cicd/fluxcd/architecture
- cicd/fluxcd/operations
- cicd/fluxcd/security
- cicd/argocd/architecture
How It Works¶
GitOps Toolkit (GOTK) controllers, pull-based reconciliation, and image automation pipeline.
Controller Reconciliation Model¶
flowchart TB
subgraph GOTK["GitOps Toolkit Controllers"]
SC["Source Controller\n(acquires artifacts)"]
KC["Kustomize Controller\n(applies Kustomizations)"]
HC["Helm Controller\n(applies HelmReleases)"]
NC["Notification Controller\n(alerts)"]
end
subgraph Sources["Sources"]
GitRepo["GitRepository\n(poll interval: 1min)"]
HelmRepo["HelmRepository"]
OCIRepo["OCIRepository"]
end
subgraph Cluster["Kubernetes Cluster"]
API["K8s API Server"]
Resources["Deployed Resources"]
end
GitRepo --> SC
HelmRepo --> SC
OCIRepo --> SC
SC -->|"artifact ready\nevent"| KC
SC -->|"chart ready\nevent"| HC
KC -->|"Server-Side Apply"| API
HC -->|"Helm SDK"| API
API --> Resources
Image Automation Pipeline¶
sequenceDiagram
participant Registry as Container Registry
participant IAC as Image Reflector Controller
participant IUA as Image Update Automation
participant Git as Git Repository
participant SC as Source Controller
participant KC as Kustomize Controller
IAC->>Registry: Poll for new image tags
IAC->>IAC: Match tag pattern (semver, regex)
IAC->>IUA: New image: myapp:v2.1.0
IUA->>Git: Push commit (update image tag in YAML)
SC->>Git: Detect new commit
SC->>KC: Trigger reconciliation
KC->>KC: Apply updated manifests
Source Controller Artifact Management¶
The source controller fetches and stores artifacts (Git commits, Helm charts, OCI blobs) as gzip-compressed tarballs in its .spec.artifact storage:
- Acquire: Clone/fetch the Git repository or pull the Helm chart
- Verify: Validate checksum (SHA256) and GPG signature (if configured)
- Package: Create a tar.gz artifact
- Store: Write to the configured storage (local disk or S3-compatible bucket)
- Notify: Emit a Kubernetes Event and update the source resource status with the artifact URL
Other controllers watch for artifact readiness events and begin their reconciliation when a new artifact is available.
Server-Side Apply¶
Both the Kustomize and Helm controllers use Kubernetes Server-Side Apply (SSA) to apply resources:
- SSA tracks field ownership via
metadata.managedFields, preventing accidental overwrites by other controllers - Conflicts are detected when two controllers manage the same field -- Flux will not overwrite fields it does not own
- This enables safe coexistence with other tools (e.g., cert-manager injecting annotations, external-dns modifying DNS records)
Reconciliation Triggers¶
Flux controllers reconcile on:
| Trigger | Controller | Mechanism |
|---|---|---|
| Poll interval | Source Controller | spec.interval (default: 1m for GitRepository) |
| Git webhook | Source Controller | HTTP endpoint receives push events, bypasses poll wait |
| Artifact ready | Kustomize/Helm Controller | Watches source resource status changes |
| Manual trigger | Any | flux reconcile kustomization <name> --force |
| Dependency | Kustomize Controller | spec.dependsOn -- waits for upstream Kustomization to be Ready |
Sources¶
Benchmarks¶
Scope
Performance characteristics, scaling limits, and resource consumption for FluxCD.
Reconciliation Performance¶
| Scale | GitRepositories | HelmReleases | Kustomizations | Reconcile Interval |
|---|---|---|---|---|
| Small | 10 | 20 | 10 | 1m |
| Medium | 50 | 100 | 50 | 5m |
| Large | 200 | 500 | 200 | 10m |
Resource Consumption¶
| Component | CPU (idle) | CPU (reconcile) | Memory |
|---|---|---|---|
| source-controller | 50m | 500m | 256Mi |
| kustomize-controller | 50m | 300m | 256Mi |
| helm-controller | 50m | 500m | 512Mi |
| image-automation | 20m | 200m | 128Mi |
Scaling Limits¶
| Dimension | Tested | Recommended | Bottleneck |
|---|---|---|---|
| GitRepositories per cluster | 500 | 200 | Source controller memory |
| HelmReleases per cluster | 1,000 | 500 | Helm controller CPU |
| Kustomizations per cluster | 500 | 200 | API server load |
Sourcing Status¶
Unsourced Performance Data
The performance numbers in this document are estimated from vendor documentation, community benchmarks, and engineering judgment. They do not represent controlled benchmarks with documented test conditions. Specific hardware configurations, software versions, and test methodologies were not recorded.
Use these figures as rough guidance only. For production capacity planning, run your own benchmarks against your specific workload and infrastructure.