Skip to main content
reliability · kubernetes

HPAs pinned with minReplicas equal to maxReplicas

resource types
1
rule IDs covered
3
severity
low

What does ZopNight detect here?

ZopNight flags a Kubernetes HorizontalPodAutoscaler on EKS, GKE or AKS whose `minReplicas` equals `maxReplicas` at a value above 1, such as 4 and 4. An HPA with no range between its bounds holds the workload at one fixed size, so it adds no elasticity and hides the fact that the replica count is really hard-coded.

Signal and threshold

How ZopNight evaluates HPAs pinned with minReplicas equal to maxReplicas.
Field Value
Rule IDsRC-1744 · RC-1844 · RC-1944
Categoryreliability
Severitylow
Metricspec.minReplicas and spec.maxReplicas
Thresholdmin equals max, max greater than 1
SourceZopNight
Permissions usedlist horizontalpodautoscalers.autoscaling

A range of one value is not autoscaling

The HPA controller picks a replica count from its metrics and then clamps it between minReplicas and maxReplicas, as the horizontal pod autoscaling page describes. When the two bounds are the same, the clamp always wins. Whatever the metrics say, the answer is the pinned number.

A pinned HPA costs in two directions. At quiet times the workload runs its full count, paying for pods that a real range would have removed. At peaks it cannot add capacity. It also misleads: the HPA manages the target’s replicas field, so someone who edits the Deployment’s replica count will see it overwritten and conclude the autoscaler is doing its job.

Finding pinned HPAs

Terminal window
kubectl get hpa -A -o json | jq -r '
.items[]
| select(.spec.minReplicas == .spec.maxReplicas and .spec.maxReplicas > 1)
| "\(.metadata.namespace)/\(.metadata.name) pinned at \(.spec.maxReplicas)"'

kubectl get hpa -A also shows MINPODS and MAXPODS side by side for a quick scan.

Equal bounds above one

ZopNight needs both bounds to be reported. It fires when they are equal and the maximum is not 1; for example, 3 and 3 fires, 2 and 5 does not. The case of 1 and 1 is excluded on purpose, because HPA cannot scale already reports any HPA with a maximum of 1. The finding names the pinned count. There is no metric or time window.

When the rule does not apply

An HPA with only a maximum set gets the default minimum of 1, so it can only be pinned at 1 and 1, which this rule leaves to the maximum-of-1 check. The rule cannot tell a temporary pin from a permanent one. HPAs held at the top of a real range are reported by HPA at max capacity instead.

A low-severity reliability finding

The recommendation carries no savings estimate. Any saving from restoring a range depends on how far below the pinned count the workload would scale at quiet times, which ZopNight does not model here.

Restoring a range, or removing the HPA

  1. Check the manifest history to see whether the pin was a deliberate choice or a leftover from an incident.
  2. To autoscale, lower the minimum to what the service needs at its quietest, for example kubectl patch hpa <name> -p '{"spec":{"minReplicas":2}}', keeping the maximum as the peak.
  3. To run a fixed size, delete the HPA and set replicas on the Deployment or StatefulSet directly, so the manifest states the real behaviour.

See it fire on your bill.

Connect an account read-only. The first findings land in minutes.

472 rule families across 353 resource types on 22 platforms. Every threshold, metric, and IAM action is documented on these pages before you grant anything.

472 rule families documented
353 resource types covered
read-only default access level
Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 20–60% off the bill, first month· 4 platforms · 1 console· Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 20–60% off the bill, first month· 4 platforms · 1 console·