Flux 2.8 GA — Helm v4 Server-Side Apply, MTTR Cuts, CEL Health Checks, and the 2026 GitOps Standard
Flux 2.8 GA — Helm v4 Server-Side Apply, MTTR Cuts, CEL Health Checks, and the 2026 GitOps Standard Published on ManoIT Tech Blog (Korean original). On February 24, 2026 the Flux project shipped v2.8.0 GA. This is not a minor release — Helm v4 becomes the default library inside helm-controller (server-side apply, kstatus), the CancelHealthCheckOnNewRevision feature gate is extended from kustomize-controller to helm-controller (dramatically reducing MTTR on failed rollouts), CEL health checks arrive for HelmRelease, ArtifactGenerator now handles Helm charts, notification-controller can comment on pull requests, Cosign v3 lands in the core, and three v1beta2/v2beta2 APIs are removed. This post walks through every one of those changes with working YAML, compares Flux 2.8 against ArgoCD 3.3, and ends with a production adoption checklist. The CNCF cloud-native survey puts GitOps adoption at 91% among cloud-native organizations in Q1 2026, with 64% of enterprises running GitOps as their "primary delivery mechanism." ArgoCD still leads overall installs (over 45% of GitOps users), but Flux holds a decisive lead where it matters most in 2026: edge, multi-cluster, and resource-constrained environments, thanks to its ~200–400 MB memory footprint and its pull-only topology with no inbound networking. Dimension Flux 2.8 (2026-02) ArgoCD 3.3 (2026-02) Implication Built-in UI None (Flux Operator Web UI is separate) Integrated (SSO + RBAC) Non-engineer access → ArgoCD Memory (steady state) 200–400 MB 500 MB–1 GB+ 500+ clusters or edge → Flux Helm handling Native delivery (Helm v4) Manifest generator (helm template) In-place chart management → Flux Lifecycle hooks PreSync / PostSync (Kustomization) Adds PreDelete (3.3) Heavy teardown → ArgoCD Failed-rollout MTTR CancelHealthCheckOnNewRevision for helm + kustomize Timeout + manual hook Long rollouts → Flux 2.8 Image automation → Git commit image-reflector + image-automation (GA in 2.7) Separate Argo Image Updater Integrated loop → Flux CEL health checks Kustomization + HelmRelease (2.8) Limited Custom-resource readiness → Flux OCI signature verification Cosign v3 (2.8) Cosign via plugin Supply-chain policy in core → Flux The broader thesis: Flux 2.8 is no longer "a GitOps tool." It is a natural extension of the Kubernetes control plane. Server-side apply as the default, kstatus-based readiness, CEL expressions, and Cosign v3 are all directions that upstream Kubernetes has already committed to. Flux exposes them consistently at the GitOps layer so you inherit the same semantics without writing them yourself. The headline change in Flux 2.8 is that helm-controller now embeds the Helm v4 library. Helm v3 relied on client-side three-way merges; v4 is rebuilt around Kubernetes server-side apply (SSA). Three concrete gains from Flux's perspective: Behavior Helm v3 + Flux ≤ 2.7 Helm v4 + Flux 2.8 Real-world effect Field merge owner Client-side three-way merge API server owns managed fields Fewer conflicts when multiple controllers touch the same object Drift detection Imprecise (relies on last-applied annotation) Accurate (ManagedFields) Policy-driven vs. ad-hoc changes are distinguishable Health checks Helm's legacy ready logic kstatus (same as kustomize-controller) Unified readiness across Deployment / StatefulSet / Job Rollback SSA Not available `.serverSideApply: auto \ enabled \ Resource recreation {% raw %}.recreate: true supported .recreate deprecated, ignored Removed in a future release, warning logged # HelmRelease — in Flux 2.8, SSA and kstatus are the default apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: name: app-payment namespace: prod-payment spec: interval: 5m chart: spec: chart: payment version: "3.14.x" sourceRef: kind: HelmRepository name: internal-charts namespace: flux-system # Helm v4: SSA also used on rollback rollback: serverSideApply: auto # auto | enabled | disabled force: false # .recreate is deprecated — do not set it values: replicaCount: 3 resources: requests: { cpu: "200m", memory: "256Mi" } limits: { cpu: "500m", memory: "512Mi" } ⚠️ Warning: Helm v4 is embedded inside helm-controller, but your chart files (Chart.yaml, templates/*) stay Helm v3–compatible — you do not have to rewrite them. What you do have to do is strip .recreate: true from every HelmRelease before upgrading. v2.8 will ignore the field and log a warning; if your CI treats warnings as errors, PRs will break. CancelHealthCheckOnNewRevision — the MTTR cheat code, now on helm-controller too The most frustrating moment in a GitOps workflow is when you've already pushed the fix but the controller keeps waiting for the previous, known-broken revision to time out before it looks at your new commit. That wait time is your MTTR. Flux 2.7 introduced the CancelHealthCheckOnNewRevision feature gate for kustomize-controller. Flux 2.8 extends it to helm-controller, and broadens the triggers. The gate now reacts not only to Git revision changes, but to spec changes (values, chart version, images), changes in referenced ConfigMaps/Secrets, and manual reconcile requests. # Enable the feature gates on helm-controller apiVersion: apps/v1 kind: Deployment metadata: name: helm-controller namespace: flux-system spec: template: spec: containers: - name: manager args: - --feature-gates=CancelHealthCheckOnNewRevision=true # Strongly recommended alongside the cancel gate - --feature-gates=DefaultToRetryOnFailure=true The Flux team recommends enabling both gates together: without DefaultToRetryOnFailure, a cancelled HelmRelease can get stuck because Helm's default retry policy is "none." Two other constraints to remember: this optimization only applies to the poller health-check strategy, and Helm hooks and tests are never cancelled, so any pre-/post-install hook you rely on still runs to completion. ⚠️ Warning: Both gates are opt-in in 2.8 and will likely flip to on-by-default once stable across both controllers. In production, enable them in staging first, run for 1–2 weeks, and validate behavior for legacy charts that lean on Helm hooks. kstatus is great for standard workloads (Deployment, StatefulSet, Job) but it cannot understand the readiness semantics of custom resources. Flux has supported CEL-based health checks on Kustomization since 2.5; Flux 2.8 brings the same expression engine to HelmRelease. # HelmRelease using CEL to track a custom-resource readiness field apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: name: postgres-operator-pg-prod namespace: database spec: interval: 3m chart: spec: chart: postgres-operator version: "2.8.x" sourceRef: { kind: HelmRepository, name: zalando, namespace: flux-system } healthCheckExprs: - apiVersion: acid.zalan.do/v1 kind: postgresql failed: status.PostgresClusterStatus == "CreateFailed" inProgress: status.PostgresClusterStatus in ["Creating", "Updating"] current: status.PostgresClusterStatus == "Running" values: configGeneral: enable_pgversion_env_var: "true" The practical payoff is that Flux actually knows when the release is done, not when Helm thinks it's done. For operator-shaped charts — databases, message brokers, vector stores — that difference eliminates a class of false-positive Ready states that used to silently break downstream Kustomizations and alerts. ArtifactGenerator (the source-watcher component) was introduced in Flux 2.7 to compose multiple Git / OCI / Bucket sources into a single deployable artifact. Flux 2.8 adds Helm chart extraction and modification, so you can declaratively pull an upstream chart, patch its values and templates in-flight, and republish it to your internal OCI registry. # Fetch an upstream chart, apply corporate patches, publish to an internal OCI registry apiVersion: source.toolkit.fluxcd.io/v1 kind: ArtifactGenerator metadata: name: patched-ingress-nginx namespace: flux-system spec: interval: 30m sources: - name: upstream kind: HelmRepository chart: ingress-nginx version: "4.11.x" source: kind: HelmRepository name: ingress-nginx transform: helm: valuesFiles: - name: corp-defaults configMapRef: { name: corp-helm-defaults, key: values.yaml } patches: - target: { kind: Deployment, name: ingress-nginx-controller } patch: |- - op: add path: /spec/template/spec/tolerations value: - key: dedicated operator: Equal value: ingress effect: NoSchedule publish: ociRepository: url: oci://registry.mano.internal/charts tag: ingress-nginx-corp The value proposition: don't fork, transform. Forks drift; ArtifactGenerator re-fetches the upstream on every interval and reapplies the same declarative patch. Drift only exists in the transform spec, which is reviewable in Git. notification-controller grew three new provider types: githubpullrequestcomment, gitlabmergerequestcomment, and giteapullrequestcomment. Flux events — failed Kustomization builds, failed HelmRelease health checks, automated image updates — can now be posted directly on the pull request, keeping the PR as the single source of truth instead of a detached Slack channel. # Comment on PRs — 2.8 also auto-resolves GitHub App installation IDs apiVersion: notification.toolkit.fluxcd.io/v1beta3 kind: Provider metadata: name: github-pr-comments namespace: flux-system spec: type: githubpullrequestcomment address: https://github.com/manoit-corp/infra secretRef: name: github-app-auth # app ID + private key; installation ID auto-resolved --- apiVersion: notification.toolkit.fluxcd.io/v1beta3 kind: Alert metadata: name: alert-on-reconcile-failure namespace: flux-system spec: providerRef: name: github-pr-comments eventSeverity: error eventSources: - kind: Kustomization name: "*" - kind: HelmRelease name: "*" The automatic installation-ID lookup for GitHub Apps matters in multi-repo estates, where tracking installation IDs manually was a quiet source of pain. "The CRD isn't installed yet and the controller explodes" is the oldest problem in GitOps. The historical fix was to split resources across multiple Kustomizations chained with dependsOn. Flux 2.8 lets you declare ordered apply stages inside a single Kustomization. # Apply in order: CRDs → Namespaces → RBAC → workloads, all in one Kustomization apiVersion: kustomize.toolkit.fluxcd.io/v1 kind: Kustomization metadata: name: platform-stack namespace: flux-system spec: interval: 5m sourceRef: { kind: GitRepository, name: platform-infra } path: ./overlays/prod prune: true applyStages: - name: crds selector: kind: CustomResourceDefinition wait: true - name: namespaces selector: kind: Namespace wait: true - name: rbac selector: kind: ["ClusterRole", "ClusterRoleBinding", "Role", "RoleBinding"] wait: false - name: workloads default: true This is especially useful for service meshes, operator bundles, and Cluster API provisioning, where "install the CRDs, then the namespaces, then the RBAC, then the controllers" used to require three or four linked Kustomizations. Flux 2.8 supports Cosign v3, which brings a TUF v2 client, stricter signature schemas, and Rekor v2 compatibility. Verification covers OCI artifacts, container images, Helm charts, and Flux's own Git commits — so end-to-end signing from source to deployed workload becomes a first-class path. # Require Cosign v3 keyless verification on an OCIRepository apiVersion: source.toolkit.fluxcd.io/v1 kind: OCIRepository metadata: name: mano-platform namespace: flux-system spec: interval: 2m url: oci://registry.mano.internal/platform ref: tag: "2026.04.21" verify: provider: cosign matchOIDCIssuer: https://token.actions.githubusercontent.com matchOIDCIssuerRegExp: "^https://token\\.actions\\.githubusercontent\\.com$" matchIdentity: "https://github.com/manoit-corp/platform/.github/workflows/release.yaml@refs/tags/*" secretRef: name: cosign-pub # TUF root included With keyless OIDC matching, you don't store signing keys at all — the GitHub Actions workflow run itself becomes the signing identity. For regulated industries pursuing SLSA v1.1 Build L3, this is becoming the reference stack. .status.inventory — full visibility into what got deployed Every HelmRelease now records the full set of managed resources under .status.inventory. Before 2.8 you had to shell into a pod and run helm get manifest to list them — that sidecar is gone. # List every resource managed by a HelmRelease kubectl -n database get helmrelease postgres-operator-pg-prod \ -o jsonpath='{.status.inventory.entries[*].id}' | tr ' ' '\n' # Example output # database_postgres-operator-pg-prod_apps_Deployment # database_postgres-operator-pg-prod_v1_Service # database_postgres-operator-pg-prod_v1_ConfigMap # database_postgres-operator-pg-prod_acid.zalan.do_postgresql Flux Operator Web UI and any kube-state-metrics pipeline can now read this field directly, so teams that were running custom inventory extractors can drop that dependency. Flux 2.8 removes three API versions from the CRDs. This is the largest compatibility event since Flux 2.0 GA — if a single v1beta2 / v2beta2 resource remains in the cluster or in Git, the upgrade is blocked. Removed API Replacement Affected resources source.toolkit.fluxcd.io/v1beta2 v1 (GA since 2.3) GitRepository, HelmRepository, OCIRepository, Bucket kustomize.toolkit.fluxcd.io/v1beta2 v1 (GA since 2.3) Kustomization helm.toolkit.fluxcd.io/v2beta2 v2 (GA since 2.5) HelmRelease # Pre-upgrade — scan the repo and the cluster for legacy API versions flux check --pre grep -r -E "apiVersion:\s*(source|kustomize|helm)\.toolkit\.fluxcd\.io/(v1beta2|v2beta2)" ./ \ || echo "OK — no legacy API versions in the repo" # Check CRD stored versions kubectl get crd gitrepositories.source.toolkit.fluxcd.io \ -o jsonpath='{.status.storedVersions}' ⚠️ Warning: Even after your manifests are on v1/v2, the CRD's status.storedVersions may still list v1beta2 — meaning etcd still holds old versions of existing objects. Use flux migrate (or the kubectl migrate recipe from the official upgrade guide) to rewrite every object to the new storage version before removing v1beta2 from the CRD spec. Skip this and the controllers fail to reconcile. The official recommendation is a two-hop path: current → 2.7 → 2.8. Flux 2.5's support window closes with this release, so 2.5 users are on the clock. # 1 — current version flux version # 2 — install the new CLI curl -s https://fluxcd.io/install.sh | sudo bash flux version --client # 2.8.x # 3 — pre-flight check (legacy APIs, Kubernetes 1.33+) flux check --pre # Flux 2.8 supports Kubernetes 1.33–1.35 and OpenShift 4.20 # 4 — re-run bootstrap with the same parameters flux bootstrap github \ --owner=manoit-corp \ --repository=fleet-infra \ --branch=main \ --path=clusters/prod \ --personal=false \ --read-write-key # 5 — verify controller health flux get all -A kubectl -n flux-system get deploy \ -o=jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.template.spec.containers[0].image}{"\n"}{end}' # 6 — opt into the MTTR feature gates # Add to helm-controller / kustomize-controller args: # --feature-gates=CancelHealthCheckOnNewRevision=true # --feature-gates=DefaultToRetryOnFailure=true For clusters on 2.5 or earlier, do not jump straight to 2.8. Go 2.6 → 2.7 → 2.8, leave at least 30 minutes of stabilization between hops, and verify flux get all -A is fully Ready before moving on. GitHub Discussion #5572 maintains the official version-matrix. Both shipped within days of each other. The selection question compresses into five dimensions: UI need, resource budget, Helm handling, PreDelete requirement, multi-cluster visibility. Workload / condition Recommendation Why Single central cluster + non-engineer dashboard needed ArgoCD 3.3 Built-in UI, SSO, mature project RBAC 500+ clusters, edge, IoT Flux 2.8 Tiny memory, pull model, no inbound network Helm-centric delivery Flux 2.8 Helm v4 native, inventory, CEL health checks Heavy teardown / PreDelete hooks ArgoCD 3.3 PreDelete hook newly supported OCI supply-chain signing is mandatory Flux 2.8 Cosign v3 with keyless OIDC in core Image auto-update → Git auto-commit loop Flux 2.8 image-reflector / image-automation (GA in 2.7) Production DR + multi-tenant visibility ArgoCD 3.3 ApplicationSet, central UI Coupled with PaaS / IDP (Backstage, EKS Capabilities) ArgoCD 3.3 Richer plugin ecosystem PR-based promotion flow needs GitOps events surfaced Flux 2.8 PR-comment notification providers You don't have to pick one. A common 2026 enterprise pattern is ArgoCD 3.3 at the central production plane + Flux 2.8 on edge and remote sites, both pointing at the same Git repo with cluster-label separation. Upgrade Kubernetes to 1.33+ first. Flux 2.8 supports 1.33–1.35; earlier versions are rejected. Scan for legacy APIs. grep -r "v1beta2\|v2beta2" across the fleet repo; clean storedVersions in CRDs too. Hop through 2.7. 2.5 / 2.6 → 2.7 → 2.8, not a direct jump. Verify Ready between hops. Enable the MTTR feature gates. CancelHealthCheckOnNewRevision=true with DefaultToRetryOnFailure=true. Remove .recreate. Strip it from every HelmRelease. Add a CI grep rule to block regressions. Adopt CEL health checks for operator-shaped charts (DBs, brokers, vector DBs) to kill false-positive Ready. Make Cosign v3 mandatory. Every OCIRepository carries verify.provider: cosign with keyless OIDC identity matching. Wire PR-comment notifications. Kustomization / HelmRelease failures post to the PR; Slack stays as a backup channel. Use Custom SSA apply stages for operator bundles that need CRD → Namespace → RBAC → workload ordering. Expose HelmRelease .status.inventory in kube-state-metrics and build a Grafana dashboard on top. Flux 2.8 moves the project's identity from "a GitOps tool" to "the deployment standard that extends Kubernetes." Helm v4 with server-side apply, kstatus, CEL, Cosign v3 — none of these are Flux ideas. They are Kubernetes directions that Flux composes consistently at the GitOps layer. Extending CancelHealthCheckOnNewRevision to helm-controller isn't a speed win; it changes the operational contract — a failed deployment no longer waits forever to be replaced by its fix. Helm support in ArtifactGenerator plus PR-comment notifications closes the loop across Git repo, pull request, and cluster, so the same state is visible on all three surfaces at once. For the ManoIT platform team, Flux 2.8 becomes the default GitOps engine for edge and multi-cluster workloads in Q2 2026. With Flux 2.5 support ending, the upgrade is no longer optional — it's a lifecycle deadline. Yes, the v1beta2 removals are a real breaking change, but the payoff (MTTR, Helm v4, CEL, Cosign v3, PR comments) is worth the hop. The question is no longer "should we adopt GitOps" — it's "how do we raise operational trust inside it." Flux 2.8 is this year's most concrete answer. Sources cross-verified: Flux 2.8 GA announcement — fluxcd.io/blog/2026/02/flux-v2.8.0/ Flux 2.7 GA announcement — fluxcd.io/blog/2025/09/flux-v2.7.0/ helm-controller CHANGELOG — github.com/fluxcd/helm-controller Flux issue #5584 (MTTR reduction) — github.com/fluxcd/flux2/issues/5584 Flux discussion #5572 (upgrade matrix) — github.com/fluxcd/flux2/discussions/5572 ArgoCD 3.3 release coverage — infoq.com/news/2026/02/argocd-33/ AWS Prescriptive Guidance on Argo CD & Flux — docs.aws.amazon.com/prescriptive-guidance/latest/eks-gitops-tools/use-cases.html Originally published at ManoIT Tech Blog.
