Startup Boost¶
Some applications need significantly more CPU during startup than at steady state. JVMs perform class loading and JIT compilation, ML models load weights into memory, and interpreted runtimes compile bytecode. Rather than over-provisioning permanently, StartupBoost applies a temporary CPU multiplier that expires after a configurable duration.
How it works¶
Startup boost has two apply paths.
CREATE webhook. When a matching policy has a recommendation and
initialSizing is on, the mutating webhook writes
recommended_cpu * multiplier onto the new pod. Job and CronJob
pods are admitted at the steady recommendation only. Boost never
applies there because batch pods are not resized in place, so
expiry cannot run. Recommend mode CREATE is limited to CronJob
and Job owners. In RequestsAndLimits mode the webhook raises
the CPU dest with the boosted request only when rec dest is
non-zero, so Guaranteed pods still get headroom. A zero rec dest
dest-caps leftover dest only and does not invent dest.
In-place resize. After the pod is running, the operator detects a
newly created pod whose age (now minus pod.CreationTimestamp) is
still within startupBoost.duration. Container start time is not used.
It resizes each eligible container's CPU request to
recommended_cpu * multiplier. Native sidecars (init containers with
restartPolicy: Always) are included. Known sidecar names such as
istio-proxy stay excluded via EffectiveExcludedContainers.
RequestsAndLimits raises dest with the boosted request only when
rec dest is non-zero, the same way CREATE does. A zero rec dest
dest-caps leftover dest only.
After a successful apply, the operator writes
attune.io/startup-boost-at with a metadata merge patch, so a
kubelet status update does not clear the stamp. The same patch writes
attune.io/startup-boost-containers, the containers that were boosted
or already at the boost target. Expiry resizes only those containers
back to the steady-state recommendation. A container that was above
the target is not in the list, so expiry does not shrink it. A stamp
from an older release has no list and still expires every container.
The boost expires when that timestamp plus duration elapses.
Container Ready is not checked.
excludeFromHistory is separate from the boost apply. When it is true,
CPU samples from the boost window are left out of the percentile. The
window starts at attune.io/startup-boost-at when that stamp is set
and not before pod creation. A missing or unreadable stamp starts at
CreationTimestamp. The cutoff is that start plus duration plus the
rate window. Sample timestamps are the end of rate(), so the rate
window is part of the cutoff. A sample exactly at the cutoff stays.
Nil and false keep today's percentile. Memory samples are unchanged.
Deleted pods stay in history until historyWindow, because there is
no creation time or stamp to cut on. A recreated pod keeps samples
older than its new creation time. A series with no pod label is left
unfiltered. Prometheus uses max by (pod, container) for that CPU
query so each pod and container is one series. If that query is
larger than --max-prometheus-series, each pod is represented before
any pod gets a second series, and the series chosen for a pod is the
container with fewer series kept. A cap smaller than the number of
containers leaves some containers without CPU samples.
If the boosted CPU would exceed maxAllowed or the node's allocatable
CPU, the boost is capped. RequestsOnly also dest-caps leftover dest
and does not raise dest.
Native sidecars¶
Init containers with restartPolicy: Always follow the same apply and
expiry path as regular containers. Known sidecar names (istio-proxy,
and the rest of the built-in list) remain auto-excluded unless you set
excludeKnownSidecars: false. See
Istio integration.
Configuration¶
Add startupBoost to the CPU resource config:
apiVersion: attune.io/v1alpha1
kind: AttunePolicy
metadata:
name: jvm-api
namespace: production
spec:
targetRef:
kind: Deployment
name: jvm-api
metricsSource:
prometheus:
address: http://prometheus-server.monitoring:80
cpu:
percentile: 95
overhead: "20"
startupBoost:
multiplier: "3.0" # 3x the steady-state recommendation
duration: 2m # boost expires 2 minutes after pod creation
minAllowed: "100m"
maxAllowed: "8000m"
memory:
percentile: 99
overhead: "30"
updateStrategy:
type: Auto
cooldown: 1h
Parameters¶
| Field | Type | Constraints | Description |
|---|---|---|---|
multiplier |
string | > 1.0, <= 10.0 | Scales the recommended CPU request during startup |
duration |
Duration | >= 10s, <= 1h | Maximum time the boost remains active |
excludeFromHistory |
bool | omitted (false) | When true, drop CPU samples from attune.io/startup-boost-at when that stamp is set and not before creation, otherwise from CreationTimestamp, until that start plus duration plus rateWindow. A sample at the cutoff stays. Samples older than creation stay. Nil and false keep today's percentile. Deleted pods stay until historyWindow. |
Choosing a multiplier¶
| Workload type | Typical multiplier | Typical duration |
|---|---|---|
| Spring Boot / JVM | 2.0 - 3.0 | 1m - 3m |
| .NET / ASP.NET Core | 1.5 - 2.0 | 30s - 1m |
| ML model loading (PyTorch, TensorFlow) | 3.0 - 5.0 | 2m - 5m |
| Node.js / Python (light init) | 1.5 | 30s |
Start conservative (2.0x, 2m) and increase if startup is still slow. Check container startup time in your monitoring to calibrate duration.
Interaction with bounds¶
The boost is applied after bounds clamping. If the boosted value exceeds
the configured maxAllowed, it is capped at maxAllowed. Set maxAllowed
high enough to accommodate the boosted value if you want the full multiplier
effect. With cpu.controlledValues: RequestsAndLimits, dest is raised
with the boosted request only when rec dest is non-zero, so Guaranteed
pods keep request equal to dest and still receive headroom. A zero rec
dest dest-caps leftover dest only and does not invent dest.
RequestsOnly dest-caps leftover dest and does not raise dest.
For example, with a 500m recommendation, 3.0x multiplier, and maxAllowed of 1000m, the boosted request will be 1000m (capped), not 1500m.
Monitoring¶
The operator tracks startup boost activity through the metric:
attune_startup_boost_total
This counter increments each time a startup boost is applied. Use it to
track how often boosts fire and whether duration covers the startup
window (expiry is attune.io/startup-boost-at plus duration, not
container Ready).
The pre-built Grafana dashboard includes a Startup Boost panel that visualizes this metric.
Limitations¶
- StartupBoost only applies to CPU. Memory startup spikes are handled by the standard overhead and percentile configuration.
- The boost requires the operator to be running when pods start. If the operator is down during a deployment, pods start with their current requests and receive the boost on the next reconcile (if still within the duration window).
- In-place boost requires a resize-capable mode (Auto, OneShot, or Canary). CREATE webhook boost also applies in Recommend mode when the pod owner is a CronJob or Job, because those pods cannot be resized in place. Observe mode never applies a boost.
See examples/14-startup-boost.yaml for a complete example.