Ballast and Attune: automating vertical sizing without restarting your pods
The native VPA kills pods to resize them. Ballast smooths the recommendations, Attune applies the resize in place. Installation, tuning and guardrails for a chain that reclaims 30 to 40 % of resources.
The observation is universal: on any production cluster, declared resource requests far exceed actual consumption. A factor of three to five is nothing unusual. A developer who does not know how much their service consumes asks generously — and nobody ever comes back to correct it.
The Vertical Pod Autoscaler addresses that problem, but it carries one flaw that disqualifies it in production: to apply a new size, it deletes the pod. On a stateful service or a long-session workload, that is unacceptable.
Two components change the picture. Ballast smooths and secures the recommendations. Attune applies them in place, building on in-place resource resizing, stable since Kubernetes 1.33.
Why the VPA alone falls short
| Problem | Observed effect |
|---|---|
| Eviction as the application mechanism | Service interruption on stateful workloads |
| Reaction to spikes | A single peak inflates the recommendation lastingly |
| No notion of headroom | Recommendation glued to consumption, with no reserve |
| Conflict with the HPA | VPA and HPA on CPU contradict each other |
| Blind to budget | No global ceiling |
Ballast deals with the first four points, Attune with the first.
Installing the chain
The prerequisite fits in one line: the InPlacePodVerticalScaling feature must be on. Since 1.33 it is enabled by default in beta.
kubectl get --raw='/readyz?verbose' | grep -i inplace
# failing that, check the control plane:
# --feature-gates=InPlacePodVerticalScaling=true# Ballast values, managed by Argo CD
recommandeur:
source: prometheus
prometheusUrl: http://prometheus.observabilite.svc:9090
fenetre: 14d # two weeks cover the weekly cycle
quantileProcesseur: 0.90 # p90: rare spikes stay ignored
quantileMemoire: 0.98 # memory: near the maximum, an overrun kills
headroomProcesseur: 1.15
headroomMemoire: 1.25
smoothing:
actif: true
methode: ewma # moyenne mobile exponentielle
alpha: 0.25
variationMax: 0.30 # never more than 30 % drift per adjustment
minPeriod: 6h # minimum delay between two adjustments
gardeFous:
processeurMin: 50m
memoireMin: 64Mi
processeurMax: "8"
memoireMax: 32Gi
ratioLimiteSurDemande: 2.0The two differentiated quantiles are the most important setting. CPU compresses: a throttled service slows down. Memory does not compress: an overrun kills the container. Hence p90 on one and p98 on the other.
# values Attune
application:
mode: in-place
fallbackQueue: true # one pod at a time per deployment
attendreDisponibilite: true
respecterPDB: true
fenetres:
- jours: ["Mon","Tue","Wed","Thu"]
debut: "10:00"
fin: "16:00" # working hours: the team is around if it goes wrong
timezone: "Europe/Paris"
fallback:
surEchecInPlace: defer # never an automatic eviction
tentativesMax: 3The fallback: defer deserves emphasis. If the in-place resize fails — which happens when memory shrinks beyond what the kernel accepts — Attune defers rather than evicting. An automatic overnight eviction on a critical service is a guaranteed incident.
Declaring a policy
apiVersion: ballast.io/v1alpha1
kind: RightSizingPolicy
metadata:
name: api-commandes
namespace: production
spec:
cible:
apiVersion: apps/v1
kind: Deployment
name: api-commandes
mode: Auto
conteneurs:
- nom: application
controle: [cpu, memory]
minAutorise: { cpu: 200m, memory: 512Mi }
maxAutorise: { cpu: "4", memory: 8Gi }
- nom: sidecar-proxy
controle: [memory] # proxy CPU follows traffic, not size
contraintes:
respectHPA: true # the HPA keeps control of CPU
variationMaxParPas: 0.25
adjustmentsMaxParJour: 3
observation:
dureeMin: 7d # no decision before a week of historyThe respectHPA constraint avoids the classic conflict. When an HPA sets replica count from CPU, letting the VPA touch CPU creates an unstable loop: the VPA lowers the request, relative utilisation rises, the HPA adds replicas, per-pod load falls, the VPA lowers again.
The rule: HPA on CPU, Ballast on memory. Or HPA on a business metric, and Ballast on both.
Verify before automating
Never switch straight to Auto. The Recommend mode produces recommendations without applying them.
# CPU waste: requested minus used, per namespace
sum by (namespace) (
kube_pod_container_resource_requests{resource="cpu"}
) -
sum by (namespace) (
rate(container_cpu_usage_seconds_total{container!=""}[7d])
)
# Gap between the recommendation and the current request
(
ballast_recommendation_cpu_cores
/
on(namespace, pod, container) kube_pod_container_resource_requests{resource="cpu"}
) < 0.5The second query lists the containers requesting more than twice what they need. Start with those: maximum gain, minimum risk.
The guardrails I impose
A global ceiling per namespace. Ballast adjusts upwards as well as downwards. Without a ceiling, an abnormal load can inflate requests until the cluster saturates.
apiVersion: v1
kind: ResourceQuota
metadata:
name: ceiling-production
namespace: production
spec:
hard:
requests.cpu: "400"
requests.memory: 1600GiAn explicit exclusion for sensitive workloads. Databases, message queues, anything with sawtooth consumption.
metadata:
annotations:
ballast.io/exclude: "true"
ballast.io/exclude-reason: "consommation irrégulière, dimensionnement manuel validé"The reason annotation is not decorative: six months later, nobody remembers why the exclusion exists.
An alert on bursts of adjustment.
- alert: AjustementsBallastEnRafale
expr: sum(increase(attune_resize_total[1h])) by (namespace) > 20
for: 15m
labels: { severity: warning }
annotations:
summary: "{{ $value }} resizes in one hour on {{ $labels.namespace }}"
description: "Smoothing is not doing its job: check alpha and minPeriod."What it returns
On a mid-sized production cluster — a hundred and fifty deployments, eight hundred pods — the full rollout reclaimed 34 % of requested CPU and 28 % of memory in six weeks, without a single incident tied to resizing.
The gain is not immediate. The first week goes into observation, the second into validation in recommendation mode, and the progressive adjustment spreads over a month. That is precisely what smoothing imposes — and it is what makes the operation safe.
One final point: that gain evaporates if nobody watches. New workloads arrive with their oversized requests. Ballast only holds because it runs continuously.