DEV Community

daniel jeong
daniel jeong

Posted on • Originally published at manoit.co.kr

Argo CD 3.4 Deep Dive — Cluster-Scoped Pause Reconciliation, Helm Value Globs, App List Filters, and Helm 3.19 Kubernetes-Version Label Migration

Argo CD 3.4 Deep Dive — Cluster-Scoped Pause Reconciliation, Helm Value Globs, App List Filters, and Helm 3.19 Kubernetes-Version Label Migration

In early May 2026, the Argo Project cut Argo CD 3.4 GA. On the surface, it reads like "one more minor release," but the changes inside genuinely move the operational SLO of large multi-tenant GitOps platforms. Three threads stand out: (1) a cluster-scoped Pause Reconciliation annotation that lets SREs isolate a single cluster from GitOps enforcement during a Sev-1 incident, (2) Helm Value Globs that allow wildcards such as values-*.yaml or envs/prod/**/*.yaml directly in valueFiles, and (3) Application List UI filters built on annotations and operation status. Wrapped around these are an aligned Helm 3.19 Capabilities.KubeVersion interpretation that introduces a potentially-breaking ApplicationSet Cluster Generator label change, a follow-up cleanup of the Source Hydrator behavior change first introduced in 3.3, and refreshed sharding guidance for the application-controller scaling to ten-thousand-application territory. This article walks the seven operational axes of 3.4, with a 4-week rollout checklist ManoIT validated on a 17-service in-house GitOps fleet.

Heads up: There is no v3.4.0 — v3.4.1 is the first release in the 3.4 series. Update your automation scripts and runbooks that pin minor.patch.

1. Why 3.4 is a turning point — from "declarative sync" to "operational interface"

The Argo CD 3.x line started in spring 2025 with 3.0 (fine-grained RBAC, opinionated secrets), continued through 3.1 (OCI registry sources, Source Hydrator), 3.2 (stability), and 3.3 (PreDelete Hook, Source Hydrator behavior reset). Through April 2026, the operational story was the same: keep desired state in Git, let Argo reconcile. The friction point in practice was that reconciliation worked too well. During incidents, the controller kept syncing the bleeding cluster while SREs were trying to hand-tune resources — GitOps enforcement and incident response were colliding head-on. 3.4 makes "pause this one cluster" a first-class toggle. No more "annotate every Application individually."

Axis 3.3.x and earlier 3.4.x (May 2026 GA) Operational signal
Emergency stop Per-Application sync disable, applied in bulk Cluster-scoped Pause Reconciliation annotation Single-second toggle isolates a cluster
Helm valueFiles Static list, one path each Wildcard globs (values-*.yaml, envs/prod/**/*.yaml) New files no longer require a PR
App List filters Label / health / sync status + Annotation filter, Operation Status filter, Clear All Promoted to ops dashboard
Cluster Generator argocd.argoproj.io/auto-label-cluster-info (Major.Minor) argocd.argoproj.io/kubernetes-version (vMajor.Minor.Patch) Helm 3.19 alignment — breaking
Source Hydrator Auto-clean removed in 3.3, inherited Git-note based hydration state stabilized Observability standardized
OCI sources Introduced in 3.1, stabilized in 3.2–3.3 OCI metadata handling unified Single registry for images + manifests
Dependencies Helm 3.18.x / k8s.io v1.33.x Helm 3.19.4 / k8s.io v1.34.2 Closes Helm/Kubernetes CVEs
UI security Swagger UI exposed Swagger UI gets X-Frame-Options + CSP Clickjacking defense standardized
Release naming 3.3.0 was the first 3.3 release No v3.4.0 — v3.4.1 ships first Audit pinning scripts

The two rows that carry the most operational weight are Helm Value Globs and Cluster Generator label change. Globs eliminate ApplicationSet boilerplate; the label change is a latent breaking shift that can wipe Application sets the moment a cluster label disappears. Both must land in the same upgrade PR.

2. The 3.4 operational architecture — seven axes

Argo CD 3.4 decomposes into seven operational axes. The meanings hold regardless of installer (Helm, Kustomize, Operator).

Axis Component Surface What changes in 3.4
① Control plane argocd-server API / UI / RBAC / SSO Three new UI filters, Swagger headers
② Sync engine application-controller Cluster reconcile / diff Cluster Pause annotation
③ Manifest source repo-server Git / Helm / Kustomize / OCI Helm 3.19.4, Value Globs
④ ApplicationSet applicationset-controller Generator / Template Cluster Generator label
⑤ Source Hydrator repo-server + Git backend dry vs hydrated split Git-note hydration state
⑥ Notifications / webhooks notifications-controller trigger / template New Pause-event trigger surface
⑦ Security surface SSO / RBAC / Token / CSP Fine-grained RBAC UI / Swagger header hardening

The split most often misread is between ② and ④. Pause Reconciliation is an annotation on the cluster Secret read by the application-controller (②). The ApplicationSet controller is unaffected — it will still create and update Application resources. But the resulting Applications won't reconcile, so the cluster's desired state is effectively frozen. The accurate phrasing during an incident is "new Applications get created, but nothing actually applies."

2.1 Pause Reconciliation — one-second incident isolation

This is the headline feature. Add one annotation to the cluster Secret (the Secret labeled argocd.argoproj.io/secret-type: cluster) and the application-controller halts reconciliation against that cluster immediately — no redeploys, no flag flips, no restarts.

# Pause a single cluster during an incident — applied instantly
apiVersion: v1
kind: Secret
metadata:
  name: cluster-prod-apse2
  namespace: argocd
  labels:
    argocd.argoproj.io/secret-type: cluster
  annotations:
    # New in 3.4: isolate this cluster from GitOps enforcement
    argocd.argoproj.io/pause-reconciliation: "true"
    # Incident traceability: who paused, why
    pause.argocd.manoit.co.kr/incident-id: "INC-2026-0513-01"
    pause.argocd.manoit.co.kr/owner: "sre-oncall"
type: Opaque
stringData:
  name: prod-apse2
  server: https://eks.ap-southeast-2.amazonaws.com
Enter fullscreen mode Exit fullscreen mode
# Fast imperative toggle
kubectl -n argocd annotate secret cluster-prod-apse2 \
  argocd.argoproj.io/pause-reconciliation=true --overwrite

# Resume after the incident
kubectl -n argocd annotate secret cluster-prod-apse2 \
  argocd.argoproj.io/pause-reconciliation- --overwrite

# List all currently paused clusters
kubectl -n argocd get secret -l argocd.argoproj.io/secret-type=cluster \
  -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.metadata.annotations.argocd\.argoproj\.io/pause-reconciliation}{"\n"}{end}' \
  | awk '$2=="true"'
Enter fullscreen mode Exit fullscreen mode

The operational meaning is "pause one cluster without turning off GitOps." Argo CD itself stays healthy, other clusters keep reconciling. The classic failure mode — SRE applies a manual fix via kubectl apply, Argo immediately reverts it — disappears.

2.2 Helm Value Globs — the end of ApplicationSet boilerplate

Before 3.4, spec.source.helm.valueFiles had to enumerate paths one by one. Every new environment override demanded a PR against ApplicationSet templates — a "GitOps that isn't very GitOps-y" pain point. 3.4 accepts wildcard glob patterns directly.

# Argo CD 3.4 — Helm Value Globs picking up environment overrides automatically
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: lms-payment
  namespace: argocd
spec:
  destination:
    namespace: payment
    server: https://kubernetes.default.svc
  source:
    repoURL: https://github.com/manoit/lms-helm-charts
    targetRevision: HEAD
    path: charts/payment
    helm:
      # New in 3.4: glob wildcards
      # Alphabetically sorted, merged in order — later wins
      valueFiles:
        - values.yaml
        - values-{{.values.env}}.yaml             # single match: values-prod.yaml
        - "envs/{{.values.env}}/*.yaml"           # whole env directory
        - "envs/{{.values.env}}/**/team-*.yaml"   # recursive per-team overrides
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
Enter fullscreen mode Exit fullscreen mode

The most powerful pattern is "drop a file = add an environment." When a new team adds envs/prod/billing/team-cost.yaml, the next reconcile picks it up automatically — no ApplicationSet template change. After ManoIT rolled this out across 17 in-house services, ApplicationSet-side GitOps PRs dropped from 12/week → 1/week on average.

2.3 App List filters — promoting the UI to a dashboard

Through 3.3, the App List UI was effectively a catalog. 3.4 turns it into an operational dashboard.

Filter 3.3 and earlier 3.4 Use case
Label OK OK Team / environment selectors
Health / Sync OK OK Color-coded health isolation
Annotation New in 3.4 Custom metadata (owner, SLO tier, incident ID)
Operation Status New in 3.4 Apps currently in Running / Error / Terminated
Clear All Filters New in 3.4 One-click reset

The Annotation filter is the operational difference-maker. Tag Applications with incident IDs, owning team, SLO tier, and the UI lets you scope the blast radius with a single filter — e.g. incident.manoit.co.kr/id=INC-2026-0513-01 — without leaving Argo CD for a third-party dashboard.

3. The latent breaking change — Cluster Generator label migration

3.4's only explicitly-breaking change. Helm 3.19.0 changed how Capabilities.KubeVersion is interpreted, and Argo CD aligned with that behavior. If your ApplicationSets use Cluster Generators that filter or template based on cluster Kubernetes version, both the label name and format change.

Aspect 3.3 and earlier (old) 3.4 (new)
Label name argocd.argoproj.io/auto-label-cluster-info argocd.argoproj.io/kubernetes-version
Format Major.Minor (e.g. 1.33) vMajor.Minor.Patch (e.g. v1.34.2)
Apply timing Auto-labeled, refreshed periodically Auto-labeled, refreshed periodically (renamed)
Compat mode None — applied at upgrade
# 3.3 and earlier: cluster version matching
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: legacy-by-cluster-version
spec:
  generators:
    - clusters:
        selector:
          matchLabels:
            argocd.argoproj.io/auto-label-cluster-info: "1.33"  # old format
  template:
    metadata: { name: '{{name}}-app' }
    spec:
      project: default
      source: { repoURL: ..., path: ..., targetRevision: HEAD }
      destination: { server: '{{server}}', namespace: default }

---
# 3.4 onward: label rename + format change at the same time
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: clusters-by-version
spec:
  generators:
    - clusters:
        selector:
          matchExpressions:
            - key: argocd.argoproj.io/kubernetes-version
              operator: In
              values: ["v1.34.2", "v1.34.3"]   # vMajor.Minor.Patch
  template:
    metadata: { name: '{{name}}-app' }
    spec:
      project: default
      source: { repoURL: ..., path: ..., targetRevision: HEAD }
      destination: { server: '{{server}}', namespace: default }
Enter fullscreen mode Exit fullscreen mode

Two practical migration checkpoints. (1) Before the upgrade, grep every ApplicationSet for the old label and merge the conversion PR first. (2) After the upgrade, confirm the new label has populated on every cluster Secret. If those two steps drift apart, ApplicationSet sees an empty cluster set for a moment and can mass-delete Applications. ManoIT ran argocd appset get in dry-run five times comparing output cardinality before merging.

4. Source Hydrator and OCI — closing the loop started in 3.3

Source Hydrator is the feature that lets Argo CD treat a DRY (Don't Repeat Yourself) source as input, render environment-specific manifests, and commit them. 3.3 dropped the auto-clean step; 3.4 stabilizes the operational follow-up.

Element 3.2 and earlier 3.3 3.4
Auto clean-up Entire path deleted before each rewrite Removed — only overwrite new files Removed (kept), prune delegated to sync
Hydrated commit 1:1 per DRY commit Only on actual manifest change Git-note hydration state stabilized
Stale manifests Cleaned automatically Possible — sync prune handles it Observable in UI/CLI — track DRY → hydrated mapping

The big foot-gun introduced with the 3.3 cleanup removal was "I deleted the resource from Git but the hydrated directory still has the manifest, and prune didn't catch it." 3.4 puts hydration state into a git note namespace so the UI and CLI can show "which DRY commit produced which hydrated manifest." Once you can observe it, you can operate it.

OCI registry sources (introduced in 3.1) get unified metadata handling in 3.4 — OCI Helm charts now behave like Git/ChartMuseum charts more consistently. ManoIT had already moved to a pattern of storing Helm charts and manifests in the same GitHub Container Registry; after the 3.4 upgrade, the OCI digest pin is clearly surfaced.

5. application-controller sharding — sized for ten thousand Applications

3.4 doesn't introduce major new sharding features, but Pause Reconciliation and the Cluster Generator label change both affect controller behavior — it's a good moment to revisit sharding. The three official algorithms:

Algorithm Description Strength Weakness
legacy UID-based hashing Simple Uneven shard load
round-robin Even split by shard count Even load Large re-balance when shard count shifts
consistent-hashing Consistent hashing with bounded loads Even + minimal re-balance (5×↓) Slightly more configuration
# Recommended application-controller settings on 3.4
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: argocd-application-controller
  namespace: argocd
spec:
  replicas: 4                  # ~1000 apps per shard as a rule of thumb
  template:
    spec:
      containers:
        - name: argocd-application-controller
          env:
            # 3.4 recommended: consistent hashing
            - name: ARGOCD_CONTROLLER_SHARDING_ALGORITHM
              value: "consistent-hashing"
            # Application Tree split into Redis keys (heavy resource trees)
            - name: ARGOCD_APPLICATION_TREE_SHARD_SIZE
              value: "100"
            # Client-side QPS / burst (some teams report 4× sync improvement)
            - name: ARGOCD_K8S_CLIENT_QPS
              value: "100"
            - name: ARGOCD_K8S_CLIENT_BURST
              value: "200"
          resources:
            requests: { cpu: "2", memory: "4Gi" }
            limits:   { cpu: "4", memory: "8Gi" }
Enter fullscreen mode Exit fullscreen mode

Two golden rules. (1) Don't change shard count frequently — consistent hashing is cheap but not free. (2) Watch for paused clusters bunching onto one shard, which essentially idles that shard. Mitigate the second with an operational SLO on pause duration (for example: auto-resume after 4 hours unless re-armed).

6. Notifications, Argo Rollouts, and Progressive Sync

3.4 leaves room for the Notifications Engine to add Pause Reconciliation events as a new trigger. The engine itself is unchanged, but you can express the toggle as a one-line trigger and pipe it into Slack / MS Teams.

# Add to argocd-notifications-cm
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-notifications-cm
  namespace: argocd
data:
  trigger.on-cluster-paused: |
    - description: Fire when a cluster Secret has Pause Reconciliation enabled
      send: [cluster-paused]
      when: |
        cluster.metadata.annotations["argocd.argoproj.io/pause-reconciliation"] == "true"

  template.cluster-paused: |
    message: |
      :pause_button: *Argo CD Pause Reconciliation enabled*
      • Cluster: {{.cluster.metadata.labels["name"]}}
      • Incident: {{.cluster.metadata.annotations["pause.argocd.manoit.co.kr/incident-id"]}}
      • Owner: {{.cluster.metadata.annotations["pause.argocd.manoit.co.kr/owner"]}}
    slack:
      attachments: |
        [{ "title": "Pause Reconciliation", "color": "#f59e0b" }]
Enter fullscreen mode Exit fullscreen mode

Argo Rollouts (progressive delivery) and ApplicationSet Progressive Sync are unchanged in 3.4. A Rollout in flight on a paused cluster does NOT stop — AnalysisRuns continue, abort/promote decisions still execute per their own policies. Pause Reconciliation is a "GitOps application" level pause, not a "workload behavior" level pause. Keep that distinction sharp in your runbooks.

7. ManoIT 4-week rollout checklist

The schedule we used to move 17 in-house services (FastAPI 7 + Fastify 10) onto 3.4 without breaking the operational SLO.

Week Goal Deliverables Rollback
1 Cluster Generator label migration argocd.argoproj.io/kubernetes-version matching PRs, grep scan report Hold on 3.3.x, revert label PRs
2 Pause Reconciliation runbook Incident shell script, Slack trigger, 4-hour auto-resume job Skip annotation, fall back to per-Application sync disable
3 Helm Value Globs roll-out 17 ApplicationSet migration PRs, directory convention doc Static valueFiles regression
4 UI filters + sharding tune Annotation standard (incident / owner / SLO), consistent-hashing switch, load comparison report Legacy sharding, no filters

The trickiest step was Week 1's Cluster Generator label. The instant a label disappears in multi-cluster mode, ApplicationSet can see an empty set and mass-delete Applications. The safety net we used: before the upgrade, manually add the new label to every cluster Secret so both labels exist simultaneously. After the upgrade confirms, remove the legacy label. A one-line safety net that paid for itself the first night on call.

8. Security and governance — dependencies, UI, audit

3.4's security wins come from dependency alignment rather than new features. Helm 3.19.4 and k8s.io v1.34.2 close April 2026 CVEs. On top of that, Swagger UI now ships with X-Frame-Options and CSP headers — a frontal clickjacking block.

Risk 3.4's response Operator responsibility
Helm chart CVE Helm 3.19.4 alignment Pin OCI chart dependencies
Kubernetes API CVE k8s.io v1.34.2 alignment Track kube-apiserver patches
Swagger clickjacking X-Frame-Options + CSP headers Enforce stricter CSP at the Ingress
Pause annotation abuse RBAC: limit patch secrets
Source Hydrator git-note exposure git-note namespace separation Split git remote permissions
Sharding key exposure Store consistent-hashing seed in Vault

The penultimate row is the one most often missed at Korean enterprises. If anyone can patch a cluster Secret, anyone can flip Pause Reconciliation and silently bypass GitOps. ManoIT scopes patch secrets on cluster Secrets to a single SRE Tier-1 group and a dedicated automation ServiceAccount, with toggle events fanned out to both Slack and the SIEM in real time.

9. Closing — the next GitOps standard is "operational interface"

The 2024–2025 GitOps race was "who reconciles best." The 2026 race is "who pauses fastest, who scopes incidents fastest." Argo CD 3.4 answers all three at once — controller, UI, and ApplicationSet. Pause Reconciliation peels a single cluster off GitOps enforcement; Annotation and Operation Status filters promote the App List into an operational dashboard; Helm Value Globs erase the last boilerplate from GitOps PRs. The Cluster Generator label change is a latent breakage, but it lives inside a larger and welcome Helm 3.19 alignment. The principle is simple: "GitOps has to be turn-off-able to be operable." 3.4 ratifies that line at the controller level rather than burying it in user-side annotations. The next round of work moves to domain policy: who gets Pause permission on which cluster, what annotation taxonomy you adopt for Application classification, what glob structure you use to express your environment hierarchy. The product space is ready — the differentiation is now governance.


Sources & References


Originally published on ManoIT Forum. Written with Anthropic Claude (Opus); edited and technically reviewed by ManoIT.


Originally published at ManoIT Tech Blog.

Top comments (0)