Skip to main content
reliability · kubernetes

HorizontalPodAutoscalers pinned at their maximum

resource types
1
rule IDs covered
3
severity
high

What does ZopNight detect here?

An HPA sitting at maxReplicas has no headroom left. It wanted to scale further and could not. ZopNight flags it as a reliability finding rather than a cost one: the workload is being throttled by a ceiling somebody set once, often months before the traffic changed.

Signal and threshold

How ZopNight evaluates HorizontalPodAutoscalers pinned at their maximum.
Field Value
Rule IDsRC-1717 · RC-1817 · RC-1917
Categoryreliability
Severityhigh
Metricnone — pure configuration read
Sourceinternal/rules/k8s/hpa_at_max.go

A pinned HPA is a silent capacity limit

An HPA at maximum is not autoscaling any more. It has computed a desired replica count above maxReplicas and been clamped. Every request beyond what the current pods can serve queues or fails, and the autoscaler that was supposed to prevent that is already doing everything it is permitted to do.

Nothing alerts on this by default. The HPA reports itself as working, because clamping is normal behaviour rather than an error state.

Why the ceiling is usually stale

maxReplicas is typically set once, at authoring time, as a guess at the upper bound plus headroom. Traffic grows; the number does not. By the time the workload is regularly hitting it, the person who chose it has often moved on.

The second common cause is a ceiling set deliberately as a cost guard, which is legitimate, and means this finding is telling you the guard is now binding rather than that it is wrong.

What to check before raising it

Raising maxReplicas only helps if the cluster can actually place more pods. Three things gate that:

  • Node capacity. Without cluster autoscaling or Karpenter, more replicas simply go Pending.
  • Resource quotas. A namespace quota on CPU or memory caps total replicas regardless of the HPA.
  • Downstream limits. More application pods against a fixed database connection pool moves the bottleneck rather than removing it, and can make things worse.

That last one matters most: scaling a stateless tier into a saturated datastore converts a slow service into a failing one.

Matching currentReplicas against maxReplicas

Terminal window
kubectl get hpa -A -o json | jq -r '
.items[] | select(.status.currentReplicas == .spec.maxReplicas)
| "\(.metadata.namespace)/\(.metadata.name)\tcur=\(.status.currentReplicas)\tmax=\(.spec.maxReplicas)"'

Sustained pinning is the signal. A brief touch during a traffic spike is the HPA working correctly.

The cost reading

Raising the ceiling increases spend, so this finding is not a saving. It is here because a workload silently capped below its demand is a reliability problem that looks like a cost control.

See it fire on your bill.

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

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

417 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·