DEV Community

daniel jeong
daniel jeong

Posted on • Originally published at manoit.co.kr

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.


1. Why Flux 2.8 matters — the GitOps landscape in 2026

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.

2. Helm v4 integration — server-side apply becomes the default

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" }
Enter fullscreen mode Exit fullscreen mode

⚠️ 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.

3. 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
Enter fullscreen mode Exit fullscreen mode

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.

4. CEL-based Helm health checks — when kstatus is not enough

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"
Enter fullscreen mode Exit fullscreen mode

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.

5. ArtifactGenerator — Helm support for the source orchestrator

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
Enter fullscreen mode Exit fullscreen mode

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.

6. Pull Request comments — closing the governance loop

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: "*"
Enter fullscreen mode Exit fullscreen mode

The automatic installation-ID lookup for GitHub Apps matters in multi-repo estates, where tracking installation IDs manually was a quiet source of pain.

7. Custom SSA apply stages — ordering CRDs, namespaces, and workloads in one Kustomization

"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
Enter fullscreen mode Exit fullscreen mode

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.

8. Cosign v3 — supply-chain policy in the core

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
Enter fullscreen mode Exit fullscreen mode

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.

9. HelmRelease .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
Enter fullscreen mode Exit fullscreen mode

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.

10. Breaking changes — the v1beta2 / v2beta2 API removals

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}'
Enter fullscreen mode Exit fullscreen mode

⚠️ 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.

11. Upgrade path — from 2.5 / 2.6 / 2.7 to 2.8

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
Enter fullscreen mode Exit fullscreen mode

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.

12. Flux 2.8 vs ArgoCD 3.3 — which side are you on

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.

13. ManoIT production checklist — ten items for Flux 2.8 adoption

  1. Upgrade Kubernetes to 1.33+ first. Flux 2.8 supports 1.33–1.35; earlier versions are rejected.
  2. Scan for legacy APIs. grep -r "v1beta2\|v2beta2" across the fleet repo; clean storedVersions in CRDs too.
  3. Hop through 2.7. 2.5 / 2.6 → 2.7 → 2.8, not a direct jump. Verify Ready between hops.
  4. Enable the MTTR feature gates. CancelHealthCheckOnNewRevision=true with DefaultToRetryOnFailure=true.
  5. Remove .recreate. Strip it from every HelmRelease. Add a CI grep rule to block regressions.
  6. Adopt CEL health checks for operator-shaped charts (DBs, brokers, vector DBs) to kill false-positive Ready.
  7. Make Cosign v3 mandatory. Every OCIRepository carries verify.provider: cosign with keyless OIDC identity matching.
  8. Wire PR-comment notifications. Kustomization / HelmRelease failures post to the PR; Slack stays as a backup channel.
  9. Use Custom SSA apply stages for operator bundles that need CRD → Namespace → RBAC → workload ordering.
  10. Expose HelmRelease .status.inventory in kube-state-metrics and build a Grafana dashboard on top.

14. Conclusion — GitOps is now the default operating mode

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.

Top comments (0)