I added a Horizontal Pod Autoscaler to a Flux-managed Deployment and watched the replica count bounce between what the HPA wanted and what Git said. Every 5 minutes, Flux would reconcile, see that spec.replicas had drifted from the Git state, and reset it. The HPA would immediately scale it back. Repeat forever.

This is a fundamental tension in GitOps: Flux’s job is to make the cluster match Git. The HPA’s job is to set the replica count based on metrics. They both write to the same field, and they disagree.

The problem Link to heading

Say your Deployment in Git has replicas: 3, and the HPA scales it to 7 based on CPU load. From Flux’s perspective, the cluster has drifted — spec.replicas is 7 but Git says 3. So Flux “fixes” it back to 3. The HPA sees CPU is still high, scales to 7 again. Next reconciliation, Flux resets it. You get a sawtooth pattern in your replica count that never stabilizes.

The obvious first step is to remove the replicas field from the Deployment manifest:

# Before
spec:
  replicas: 3
  template:
    ...

# After — let HPA own it
spec:
  template:
    ...

This helps — Flux won’t set a specific replica count if the field isn’t in the manifest. But it’s not always enough. If Flux applies the manifest and the server-side default fills in replicas: 1, you’re back to the same problem. And if you have overlay patches (like Kustomize patches for production) that set replicas, those need to be cleaned up too.

The fix: ignoreDifferences Link to heading

Flux Kustomization resources support an ignoreDifferences field that tells Flux to skip specific fields during drift detection. For the HPA case:

apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: my-app
  namespace: flux-system
spec:
  # ... other fields ...
  ignoreDifferences:
    - target:
        kind: Deployment
        namespace: my-app
      jsonPatches:
        - op: remove
          path: /spec/replicas

This tells Flux: “when comparing the desired state from Git with the live state in the cluster, pretend /spec/replicas doesn’t exist on Deployments in the my-app namespace.” Flux still manages every other field in the Deployment — image tags, resource requests, env vars, probe configuration — but it leaves spec.replicas alone.

How it works under the hood Link to heading

When Flux reconciles, it:

  1. Renders the manifests from Git (running Kustomize, substituting variables, etc.)
  2. Fetches the live state from the cluster
  3. Computes a diff between the two
  4. Applies the diff to the cluster

The ignoreDifferences directive intervenes at step 3. Before Flux computes the diff, it applies the JSON patches to the desired state (the rendered manifests from Git). The op: remove patch strips /spec/replicas from the desired state, so when Flux compares it against the live state, there’s nothing to diff on that field. Flux sees no drift and leaves it alone.

This is why you need op: remove and not something like op: replace — you’re not setting a value, you’re removing the field from Flux’s awareness entirely.

Scoping with target Link to heading

The target field controls which resources the patches apply to. You can scope it narrowly:

ignoreDifferences:
  - target:
      kind: Deployment
      namespace: my-app
      name: frontend  # only this specific Deployment
    jsonPatches:
      - op: remove
        path: /spec/replicas

Or broadly — applying to all Deployments in a namespace, which is what I used since both my CMS and frontend Deployments have HPAs:

ignoreDifferences:
  - target:
      kind: Deployment
      namespace: my-app
    jsonPatches:
      - op: remove
        path: /spec/replicas

The gotcha: remove replicas from your manifests too Link to heading

Even with ignoreDifferences, you should still remove replicas from your Deployment manifests and any Kustomize patches. If you leave replicas: 3 in the manifest, it will be applied on the first reconciliation (before the HPA has a chance to set its own value), potentially causing a brief scale-down every time Flux first applies a change to the Deployment.

The cleanest setup is:

  1. No replicas field in base Deployment
  2. No replicas in any overlay patches
  3. ignoreDifferences with op: remove on /spec/replicas in the Flux Kustomization
  4. HPA minReplicas set to your desired minimum

This way, the HPA is the single source of truth for replica count, and Flux is the single source of truth for everything else.