#10255·autoscaler

VPA: memory-aggregation-interval-count does not work as expected

Author: irbekrmCreated Sep 6, 2026Updated Sep 14, 2026
Labelsarea/vertical-pod-autoscalerkind/bugtriage/accepted

Which component are you using?:

/area vertical-pod-autoscaler

What version of the component are you using?: v1.7.1 (but I believe this is not recent and may have been introduced as far ago as https://github.com/kubernetes/autoscaler/pull/815)

Component version: registry.k8s.io/autoscaling/vpa-recommender@sha256:89cea705535f9d8df6e62d5084916ec447e85d64369cfff2f7c6ac9d1cc5cd1e

What k8s version are you using (kubectl version)?:

kubectl version Output
k version
Client Version: v1.34.1
Kustomize Version: v5.7.1
Server Version: v1.34.9-eks-bca9cf6

What environment is this in?:

EKS

What did you expect to happen?:

I expected that the flag memory-aggregation-interval-count and the corresponding API field vpa.spec.resourcePolicy.containerPolicies.memoryAggregationIntervalCount defines a count such that memory recommendation is calculated strictly from a window where window = count * interval_length. So that, for example, with the default settings where interval=24, count=8, memory usage samples from > 8 days ago are not added to bucket weights used to calculate the recommendation.

What happened instead?:

While a workload keeps running the memory usage samples keep counting towards the recommendation. If a workload is restarted with new Pod labels, then the old aggregate state built from the old samples stop counting towards the recommendation after window = count x inteval_length period, see AggregateContainerState.isExpired.

In practice, it seems to be:

  • the actual logic of memory-aggregation-interval-count seems to be 'If the workload's labels change, e.g. as a result of redeploy with new Pod hash labels, the old memory usage samples will be discarded count x interval_length period after the label change'
  • for the default settings with 24h half life, it likely does not matter too much because the samples beyond 8 days would constitute for less than 1%
  • with longer half life however, samples older than window length alone could constitute upperbound or even target. We were considering 14 day half life with 28 day count, it seems that in practice samples up to 60 days back could influence the recommendation 2^(−60/14) ~= 0.05 = 5%

How to reproduce it (as minimally and precisely as possible):

I've tested this with vpa.spec.resourcePolicy.containerPolicies.memoryAggregationIntervalCount field rather than the flag, but I believe that the flag behavior is identical.

  1. Deploy vpa with --feature-gates=PerVPAConfig=true flag
  2. Create a deployment that on startup consumes ~512MB memory for 10 minutes:
apiVersion: apps/v1 kind: Deployment metadata: name: vpa-count-test spec: replicas: 1 selector: matchLabels: {app: vpa-count-test} template: metadata: labels: {app: vpa-count-test} spec: containers: - name: app image: ubuntu command: - bash - -c - | DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install python3 -y sleep 600 # give VPA a chance to discover the Pod python3 -c 'import time; b = bytearray(512*1024*1024); time.sleep(600)' sleep infinity resources: requests: {cpu: 50m, memory: 64Mi}
  1. Create a VPA object for the Deployment with 10 minute interval and interval count of 2:
apiVersion: autoscaling.k8s.io/v1 kind: VerticalPodAutoscaler metadata: name: vpa-count-test spec: targetRef: apiVersion: apps/v1 kind: Deployment name: vpa-count-test updatePolicy: updateMode: "Off" resourcePolicy: containerPolicies: - containerName: app memoryAggregationIntervalSeconds: 600 memoryAggregationIntervalCount: 2
  1. Observe that while this should have had a 20 minute window, 2 hours later the bucket 28 that would correspond to the memory usage from the first 20 minutes is still present in the checkpoint and determines the target and upperbound:
k get vpacheckpoint vpa-count-test-app -oyaml apiVersion: autoscaling.k8s.io/v1 kind: VerticalPodAutoscalerCheckpoint metadata: creationTimestamp: "2026-09-06T11:15:02Z" generation: 25 name: vpa-count-test-app ... spec: containerName: app vpaObjectName: vpa-count-test status: cpuHistogram: bucketWeights: "0": 10000 referenceTimestamp: "2026-09-06T00:00:00Z" totalWeight: 3.560273162411728 firstSampleStart: "2026-09-06T11:14:17Z" lastSampleStart: "2026-09-06T13:14:06Z" lastUpdateTime: "2026-09-06T13:15:02Z" memoryHistogram: bucketWeights: "6": 10000 "28": 1943 referenceTimestamp: "2026-09-06T00:00:00Z" totalWeight: 17.131182927492507 totalSamplesCount: 25 version: v3 $ k get vpa vpa-count-test -oyaml ... spec: resourcePolicy: containerPolicies: - containerName: app memoryAggregationIntervalCount: 2 memoryAggregationIntervalSeconds: 600 targetRef: apiVersion: apps/v1 kind: Deployment name: vpa-count-test updatePolicy: updateMode: "Off" status: ... recommendation: containerRecommendations: - containerName: app ... target: cpu: 15m memory: "716711186" uncappedTarget: cpu: 15m memory: "716711186" upperBound: cpu: 620m memory: "40411484564"

Anything else we need to know?:

Slack chat https://kubernetes.slack.com/archives/C09R1LV8S/p1788441086365319

I think that this bug was introduced in https://github.com/kubernetes/autoscaler/pull/815 where memory histogram changed from FloatSlidingWindowHistogram to DecayingHistogram. I think a fix would need to involve re-introducing a sliding window, perhaps another histogram wrapped by the decaying one. By looking at the PR and the changes I assume that the histogram type change was a side effect of moving to per-labelset state and the interval-count was left behind.

Source: kubernetes/autoscaler