Kubernetes Autoscaling and Capacity Planning
How HPA, resource requests, and cluster capacity interact—and why a target utilization is not a capacity plan.
Understand readiness, surge capacity, graceful shutdown, and telemetry without treating rolling updates as an availability guarantee.
Kubernetes Deployments progressively replace replicas. This helps preserve service capacity while changing an application, but it cannot guarantee zero downtime on its own. Availability also depends on readiness, available cluster capacity, load balancing, shutdown behavior, dependency compatibility, and what users are actually doing.
This guide uses an illustrative three-replica application. Its numbers are configuration examples, not measured production results or promises.
An ingress controller or external load balancer receives requests and routes them toward a Service. A Service selects matching pods through labels, and its ready endpoints determine which replicas should receive traffic. The control plane reconciles desired state; worker nodes run the containers.
The scheduler needs sufficient resources to place replacement pods. A Deployment can remain stuck even when its YAML is valid if no worker has capacity for the requested CPU and memory. Image pull errors and unavailable secrets are other common causes of stalled rollouts.
Readiness asks whether the pod should receive traffic. Liveness asks whether the container should be restarted. Startup probes give slow-starting applications time to initialize before liveness checks begin. They should not be interchangeable endpoints chosen merely for convenience.
A readiness check can reflect whether the application can serve requests, but deep dependency checks require care. If every replica becomes unready during a shared database incident, the ingress may have no endpoints left. A liveness check that restarts pods when the database is unavailable may amplify the outage.
apiVersion: apps/v1
kind: Deployment
metadata:
name: example-api
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: example-api
template:
metadata:
labels:
app: example-api
spec:
terminationGracePeriodSeconds: 30
containers:
- name: api
image: example.invalid/api:v1
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /ready
port: 8080
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
memory: 512MiThe image above is an intentional placeholder and is not runnable. Replace it with a real, reviewed image digest and an application that implements the readiness endpoint. The probe configuration is a starting point; tune intervals, thresholds, startup behavior, and resource values using application measurements.
With three replicas, maxSurge: 1 permits one additional replica above desired count during the rollout, while maxUnavailable: 0 avoids deliberately reducing the available count. This requires headroom for the replacement pod. Terminating pods can consume resources while shutting down, so observed pod and resource totals may temporarily exceed the simple desired-plus-surge count.
CPU requests influence scheduling and are also the denominator for CPU-utilization-based HPA calculations. An HPA cannot produce meaningful CPU utilization targets for a workload missing relevant CPU requests. HPA changes desired replicas; a node autoscaler addresses node capacity. These are separate feedback loops with different delays.
The application should handle termination signals, stop accepting new work, and allow in-flight requests to finish within the grace period. Endpoint updates and load balancer behavior are not instantaneous. Test shutdown with real request durations and the actual ingress path, rather than assuming a sleep hook guarantees drainage.
A PodDisruptionBudget constrains supported voluntary disruptions such as eviction. It does not directly govern a Deployment controller's rolling update, and it cannot prevent involuntary failures. Use the Deployment strategy, replica placement, disruption budget, and available capacity together.
Check rollout progress, application-level smoke tests, user-facing errors, request latency, and saturation. OpenTelemetry traces can help distinguish application regressions from downstream bottlenecks, while structured logs provide request context. Avoid unbounded request IDs as Prometheus metric labels.
Old and new versions run together during a rolling update. Database migrations and API changes must tolerate this overlap. Expand-and-contract schema changes are often safer than immediately removing fields required by the previous version.
GitOps tools reconcile a declared desired state. A manual rollback that is not reflected in Git may be undone by the reconciler. Define whether recovery means reverting the Git change, promoting a corrected image, or temporarily suspending reconciliation under a documented process. Finally, test these procedures: a declarative rollback option is not the same as demonstrated recovery.
Read Kubernetes autoscaling and capacity planning for the HPA and headroom questions this rollout depends on. The Platform Engineering page walks the Deployment → Service path, and the architecture lab shows an educational rolling update.
How HPA, resource requests, and cluster capacity interact—and why a target utilization is not a capacity plan.
Build a usable telemetry model around user-facing SLIs, bounded cardinality, and alerts that have a next step.
Isolate blast radius with accounts, identities, state, and promotion of the same artifact—not a shared admin role with three VPC names.