ADR-067: Argo CD leaves the replica count of an autoscaled Deployment alone
Status: Accepted (2026-10-28) Builds on: ADR-014: gitops-app composition spec (one ApplicationSet per environment over the component overlays), ADR-017: The GitOps repo owns every Kubernetes manifest; services ship an image, ADR-066: Instances scale with a plain HPA or with KEDA, and the platform installs KEDA per cluster when an instance needs it, spec 120
Context
Section titled “Context”An autoscaler (an HPA, or the HPA that KEDA owns) writes spec.replicas of its Deployment. Argo CD applies the rendered Deployment with
automated sync and self-heal, so a rendered replicas would be reset on every sync and the two controllers would fight. The usual
remedies each break something the platform needs: omitting spec.replicas makes a client-side apply delete the field when an instance
switches from a fixed count to scale, which drops the Deployment to 1 replica; ignoring the fields that the controller manager owns
(managedFieldsManagers) keeps ignoring them after the instance switches back to a fixed count; Replace=true recreates objects on
every sync. The component ApplicationSet of an environment is one template for all its components and uses fasttemplate, so a
per-component field cannot be switched on by a parameter.
Decision
Section titled “Decision”- A scaled Deployment keeps
spec.replicas, set tomin: it is what a new Deployment starts with, and what a sync that reaches a Deployment before its ignore rule applies sets (never below the newmin). - The environment’s component ApplicationSet ignores
/spec/replicasof each scaled Deployment by name (oneignoreDifferencesentry per scaled instance of that environment) and syncs withRespectIgnoreDifferences=true, so neither the diff nor a sync (self-heal included) touches the live count. An environment without a scaled instance keeps its ApplicationSet byte for byte. - Autoscaler objects are files of the component overlay (
hpa.yaml,scaledobject.yaml,triggerauthentication.yaml), so removingscaleremoves them and Argo CD’s prune deletes them; the Deployment then gets its fixed count with the next sync because its name left the ignore list. - KEDA objects sync in wave 1, after the Deployment and after the prune of an HPA they replace, with
SkipDryRunOnMissingResource=true; an environment with a KEDA instance retries its sync (the platform’sRETRY). No sync option recreates an object.
Consequences
Section titled “Consequences”- An Argo CD application with a scaled instance stays Synced while the autoscaler works, and a switch in either direction never runs fewer
replicas than the new
min(or the new fixed count). - The ignore list names Deployments, not fields owned by a manager, so a fixed count takes effect again as soon as
scaleis removed. - KEDA’s own HPA (
keda-hpa-<component>) is a child of theScaledObjectand not tracked by Argo CD; this holds with Argo CD’s annotation tracking (the default since Argo CD 3.0). A cluster that tracks by label must switch to annotation tracking.
Not included
Section titled “Not included”- Ignoring any other field (resources, images): only the replica count is handed to the autoscaler.
- Changing the ApplicationSet to Go templates or per-component ApplicationSets (both would change every system’s rendered tree).