---
# === IDENTITY ===
id: software/devops/k8s-hpa/2026
canonical_question: "Kubernetes reference: Horizontal Pod Autoscaler"
aliases:
  - "Kubernetes HPA configuration"
  - "k8s horizontal pod autoscaler setup"
  - "HPA v2 autoscaling YAML"
  - "Kubernetes autoscale deployment pods"
  - "HPA custom metrics Prometheus adapter"
  - "KEDA vs HPA Kubernetes"
  - "kubectl autoscale CPU memory"
  - "HPA scaling behavior stabilization window"
entity_type: software_reference
domain: software > devops > Kubernetes HPA
region: global
jurisdiction: global
temporal_scope: 2024-2026

# === VERIFICATION ===
last_verified: 2026-02-27
confidence: 0.94
version: 1.0
first_published: 2026-02-27

# === TEMPORAL VALIDITY ===
temporal_validity:
  status: stable
  last_breaking_change: "autoscaling/v2 GA in Kubernetes 1.23 (Dec 2021); autoscaling/v2beta2 removed in 1.26 (Dec 2022); container resource metrics GA in 1.27 (Apr 2023)"
  next_review: 2026-08-26
  change_sensitivity: medium

# === CONSTRAINTS ===
constraints:
  - "Pods MUST have resource requests defined -- HPA calculates utilization as a percentage of requests, not limits"
  - "metrics-server MUST be deployed in the cluster for CPU/memory-based HPA to function"
  - "HPA and VPA MUST NOT target the same pods on the same resource metric (CPU/memory) -- use VPA for sizing, HPA for scaling"
  - "Use autoscaling/v2 API -- v1 only supports CPU and lacks scaling behavior controls"
  - "minReplicas MUST be >= 1 for HPA (use KEDA if you need scale-to-zero)"
  - "Custom and external metrics require a metrics adapter (e.g., prometheus-adapter) registered with the aggregation layer"

# === SKIP CONDITIONS ===
skip_this_unit_if:
  - condition: "Need to scale based on external events (Kafka queue depth, SQS messages, cron schedules)"
    use_instead: "software/devops/keda-autoscaler/2026"
  - condition: "Need to right-size pod resource requests/limits, not scale replica count"
    use_instead: "software/devops/k8s-vpa/2026"
  - condition: "Need to scale cluster nodes, not pods"
    use_instead: "software/devops/cluster-autoscaler/2026"

# === AGENT HINTS ===
inputs_needed:
  - key: "metric_type"
    question: "What metric should drive scaling?"
    type: choice
    options: ["CPU utilization", "Memory utilization", "Custom application metric", "External metric (cloud provider)", "Event-driven (queue depth)"]
  - key: "workload_type"
    question: "What type of workload are you scaling?"
    type: choice
    options: ["Stateless web service", "Stateful application", "Batch/job processor", "Event-driven consumer"]

# === DISTRIBUTION ===
canonical_source: "https://knowledgelib.io/software/devops/k8s-hpa/2026"
suggested_citation: "Source: knowledgelib.io -- AI Knowledge Library (verified 2026-02-27)"

# === RELATED UNITS ===
related_kos:
  depends_on:
    - id: "software/devops/k8s-metrics-server/2026"
      label: "Kubernetes Metrics Server Setup"
  related_to:
    - id: "software/devops/k8s-resource-management/2026"
      label: "Kubernetes Resource Requests and Limits"
    - id: "software/devops/prometheus-monitoring/2026"
      label: "Prometheus Monitoring Setup"
  solves: []
  alternative_to:
    - id: "software/devops/keda-autoscaler/2026"
      label: "KEDA Event-Driven Autoscaler"
    - id: "software/devops/k8s-vpa/2026"
      label: "Kubernetes Vertical Pod Autoscaler"
  often_confused_with:
    - id: "software/devops/k8s-vpa/2026"
      label: "Vertical Pod Autoscaler (sizes pods, does not scale replicas)"
    - id: "software/devops/cluster-autoscaler/2026"
      label: "Cluster Autoscaler (scales nodes, not pods)"

# === SOURCES (7 authoritative sources) ===
sources:
  - id: src1
    title: "Horizontal Pod Autoscaling"
    author: Kubernetes Project
    url: https://kubernetes.io/docs/concepts/workloads/autoscaling/horizontal-pod-autoscale/
    type: official_docs
    published: 2025-12-01
    reliability: authoritative
  - id: src2
    title: "HorizontalPodAutoscaler Walkthrough"
    author: Kubernetes Project
    url: https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/
    type: official_docs
    published: 2025-12-01
    reliability: authoritative
  - id: src3
    title: "prometheus-adapter: Custom Metrics API using Prometheus"
    author: Kubernetes SIGs
    url: https://github.com/kubernetes-sigs/prometheus-adapter
    type: official_docs
    published: 2025-10-01
    reliability: high
  - id: src4
    title: "KEDA - Kubernetes Event-driven Autoscaling"
    author: KEDA Project (CNCF)
    url: https://keda.sh/docs/
    type: official_docs
    published: 2025-11-01
    reliability: high
  - id: src5
    title: "Taming Kubernetes HPA Flapping with Stabilization Windows"
    author: FlightCrew
    url: https://www.flightcrew.io/blog/hpa-stabilization-window
    type: technical_blog
    published: 2025-06-15
    reliability: moderate_high
  - id: src6
    title: "Configuring horizontal Pod autoscaling"
    author: Google Cloud
    url: https://docs.cloud.google.com/kubernetes-engine/docs/how-to/horizontal-pod-autoscaling
    type: official_docs
    published: 2025-09-01
    reliability: high
  - id: src7
    title: "Configurable HPA Scale Velocity KEP-853"
    author: Kubernetes Enhancements
    url: https://github.com/kubernetes/enhancements/blob/master/keps/sig-autoscaling/853-configurable-hpa-scale-velocity/README.md
    type: official_docs
    published: 2024-01-01
    reliability: high
---

# Kubernetes Horizontal Pod Autoscaler (HPA): Complete Reference

## TL;DR

- **Bottom line**: HPA automatically adjusts pod replica count based on observed CPU, memory, custom, or external metrics using the formula `desiredReplicas = ceil(currentReplicas * (currentMetric / desiredMetric))`.
- **Key tool/command**: `kubectl autoscale deployment <name> --cpu-percent=50 --min=2 --max=10`
- **Watch out for**: Pods without resource requests defined -- HPA cannot calculate utilization percentage and will not scale.
- **Works with**: Kubernetes 1.23+ (autoscaling/v2 GA). Requires metrics-server for CPU/memory. Custom metrics need prometheus-adapter or equivalent.

## Constraints
<!-- Agents: read this section before recommending any action from this unit.
     These are hard constraints that must not be violated. -->

- Pods MUST have resource requests defined -- HPA calculates utilization as a percentage of requests, not limits
- metrics-server MUST be deployed in the cluster for CPU/memory-based HPA to function
- HPA and VPA MUST NOT target the same pods on the same resource metric (CPU/memory) -- use VPA for sizing, HPA with custom metrics for scaling
- Use autoscaling/v2 API -- v1 only supports CPU and lacks scaling behavior controls
- minReplicas MUST be >= 1 for HPA (use KEDA if you need scale-to-zero)
- Custom and external metrics require a metrics adapter registered with the aggregation layer

## Quick Reference

**HPA Metric Types:**

| # | Metric Type | API Group | Source | Target Types | Example |
|---|---|---|---|---|---|
| 1 | Resource | metrics.k8s.io | metrics-server | Utilization, AverageValue | CPU at 50% utilization |
| 2 | ContainerResource | metrics.k8s.io | metrics-server | Utilization, AverageValue | CPU of specific container |
| 3 | Pods | custom.metrics.k8s.io | prometheus-adapter | AverageValue | requests_per_second per pod |
| 4 | Object | custom.metrics.k8s.io | prometheus-adapter | Value, AverageValue | Ingress requests_per_second |
| 5 | External | external.metrics.k8s.io | cloud provider adapter | Value, AverageValue | SQS queue depth from CloudWatch |

**Scaling Behavior Configuration:**

| Parameter | Default (Scale Up) | Default (Scale Down) | Description |
|---|---|---|---|
| stabilizationWindowSeconds | 0 | 300 (5 min) | Window to pick highest/lowest recommendation |
| policies[].type | Percent / Pods | Percent / Pods | Scale by percentage or absolute pod count |
| policies[].value | 100% or 4 pods | 100% | Maximum change per period |
| policies[].periodSeconds | 15 | 60 | How often policy can trigger |
| selectPolicy | Max | Max | Pick largest (Max) or smallest (Min) of policies |

**HPA Algorithm Parameters:**

| Parameter | Default | Flag | Description |
|---|---|---|---|
| Sync period | 15s | `--horizontal-pod-autoscaler-sync-period` | How often HPA evaluates metrics |
| Tolerance | 0.1 (10%) | `--horizontal-pod-autoscaler-tolerance` | Ratio must differ from 1.0 by this much to trigger scaling |
| Downscale stabilization | 300s | Via `behavior.scaleDown.stabilizationWindowSeconds` | Prevents rapid scale-down oscillation |
| Initial readiness delay | 30s | `--horizontal-pod-autoscaler-initial-readiness-delay` | Ignore unready pods for this duration |
| CPU init period | 5m | `--horizontal-pod-autoscaler-cpu-initialization-period` | Ignore CPU metrics during startup |

## Decision Tree

> Full script: [decision-tree.txt](scripts/decision-tree.txt) (29 lines)

```
START: What type of autoscaling do you need?
|
+-- Need to scale replica count based on CPU/memory?
|   +-- YES --> Use HPA with Resource metrics (see Step 1-2 below)
|   +-- NO |
# ... (see full script)
```

## Step-by-Step Guide

### 1. Install metrics-server

metrics-server collects CPU and memory usage from kubelets and exposes them via the metrics.k8s.io API. Required for any resource-based HPA. [src1]

```bash
# Install metrics-server via kubectl
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

# For local clusters (minikube, kind) -- disable TLS verification
kubectl patch deployment metrics-server -n kube-system \
  --type='json' \
  -p='[{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"--kubelet-insecure-tls"}]'
```

**Verify**: `kubectl top nodes` --> should show CPU and memory usage for each node.

### 2. Create a basic HPA with CPU target

Deploy an HPA targeting a Deployment at 50% average CPU utilization. [src2]

```yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: my-app-hpa
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-app
  minReplicas: 2
  maxReplicas: 20
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 70
```

**Verify**: `kubectl get hpa my-app-hpa` --> TARGETS column should show current/target percentages.

### 3. Set up custom metrics with prometheus-adapter

Install prometheus-adapter to expose Prometheus metrics via the custom.metrics.k8s.io API. [src3]

```bash
# Install via Helm
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm install prometheus-adapter prometheus-community/prometheus-adapter \
  --namespace monitoring \
  --set prometheus.url=http://prometheus-server.monitoring.svc \
  --set prometheus.port=9090
```

Configure a custom metric rule in the adapter ConfigMap:

```yaml
# prometheus-adapter ConfigMap rules
rules:
  - seriesQuery: 'http_requests_total{namespace!="",pod!=""}'
    resources:
      overrides:
        namespace: {resource: "namespace"}
        pod: {resource: "pod"}
    name:
      matches: "^(.*)_total$"
      as: "${1}_per_second"
    metricsQuery: 'sum(rate(<<.Series>>{<<.LabelMatchers>>}[2m])) by (<<.GroupBy>>)'
```

**Verify**: `kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1" | jq .` --> should list custom metrics.

### 4. Configure scaling behavior (stabilization)

Fine-tune scale-up aggressiveness and scale-down conservatism to prevent flapping. [src5]

```yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: my-app-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-app
  minReplicas: 2
  maxReplicas: 50
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 60
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 30
      policies:
      - type: Percent
        value: 100
        periodSeconds: 60
      - type: Pods
        value: 4
        periodSeconds: 60
      selectPolicy: Max
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
      - type: Percent
        value: 10
        periodSeconds: 120
      selectPolicy: Min
```

**Verify**: `kubectl describe hpa my-app-hpa` --> check Events section for scaling decisions and behavior policy applied.

### 5. Create HPA with custom metrics

Scale on application-specific metrics like requests per second after setting up prometheus-adapter. [src3]

```yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: my-app-hpa-custom
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-app
  minReplicas: 2
  maxReplicas: 30
  metrics:
  - type: Pods
    pods:
      metric:
        name: http_requests_per_second
      target:
        type: AverageValue
        averageValue: "100"
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
```

**Verify**: `kubectl get hpa my-app-hpa-custom` --> both metrics should show current values.

### 6. Use KEDA for event-driven autoscaling

When you need scale-to-zero or 60+ external event source integrations beyond what HPA offers natively. [src4]

```bash
# Install KEDA via Helm
helm repo add kedacore https://kedacore.github.io/charts
helm repo update
helm install keda kedacore/keda --namespace keda --create-namespace
```

```yaml
# KEDA ScaledObject -- scales based on RabbitMQ queue depth
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: rabbitmq-consumer
spec:
  scaleTargetRef:
    name: consumer-deployment
  minReplicaCount: 0        # Scale to zero when idle
  maxReplicaCount: 30
  pollingInterval: 15
  cooldownPeriod: 300
  triggers:
  - type: rabbitmq
    metadata:
      host: amqp://guest:guest@rabbitmq.default.svc:5672/
      queueName: task-queue
      queueLength: "5"       # 5 messages per pod
```

**Verify**: `kubectl get scaledobject rabbitmq-consumer` --> READY should be True.

## Code Examples
<!-- Keep inline examples <=15 lines. For longer scripts, extract to scripts/ subdirectory
     and link: "Full script: [name.ext](scripts/name.ext) (N lines)" -->

### YAML: Multi-Metric HPA with External Metrics

> Full script: [yaml-multi-metric-hpa-with-external-metrics.yml](scripts/yaml-multi-metric-hpa-with-external-metrics.yml) (49 lines)

```yaml
# Input:  Deployment "api-server" running in production
# Output: HPA scaling on CPU, memory, and AWS SQS queue depth
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
# ... (see full script)
```

### Python: Kubernetes Client HPA Management

> Full script: [python-kubernetes-client-hpa-management.py](scripts/python-kubernetes-client-hpa-management.py) (38 lines)

```python
# Input:  Kubernetes cluster with kubeconfig configured
# Output: Creates/updates an HPA programmatically
from kubernetes import client, config  # kubernetes==29.0.0
config.load_kube_config()
autoscaling_v2 = client.AutoscalingV2Api()
# ... (see full script)
```

### Go: HPA Status Monitoring

> Full script: [go-hpa-status-monitoring.go](scripts/go-hpa-status-monitoring.go) (41 lines)

```go
// Input:  Kubernetes cluster access
// Output: Prints current HPA status for all HPAs in a namespace
package main
import (
    "context"
# ... (see full script)
```

## Anti-Patterns

### Wrong: HPA without resource requests

> Full script: [wrong-hpa-without-resource-requests.yml](scripts/wrong-hpa-without-resource-requests.yml) (29 lines)

```yaml
# BAD -- HPA cannot calculate utilization without resource requests
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
# ... (see full script)
```

### Correct: Always define resource requests

```yaml
# GOOD -- resource requests let HPA calculate utilization percentage
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  template:
    spec:
      containers:
      - name: app
        image: my-app:latest
        resources:
          requests:
            cpu: 200m
            memory: 256Mi
          limits:
            cpu: 500m
            memory: 512Mi
```

### Wrong: Using autoscaling/v1 for production

```yaml
# BAD -- v1 only supports CPU, no scaling behavior, no custom metrics
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: my-app-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-app
  minReplicas: 1
  maxReplicas: 10
  targetCPUUtilizationPercentage: 50
```

### Correct: Use autoscaling/v2 with behavior controls

```yaml
# GOOD -- v2 with scaling behavior prevents flapping
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: my-app-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-app
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300
```

### Wrong: HPA and VPA both targeting CPU on same pods

```yaml
# BAD -- HPA and VPA conflict on the same resource metric
# HPA wants more pods; VPA wants to resize the same pods
# Result: oscillation, pods restarting, unpredictable behavior
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
spec:
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50
---
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
spec:
  targetRef:
    name: my-app  # Same deployment!
  resourcePolicy:
    containerPolicies:
    - containerName: app
      controlledResources: ["cpu"]  # Same resource!
```

### Correct: VPA for sizing, HPA on custom metric

```yaml
# GOOD -- VPA manages resource requests; HPA scales on custom metric
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
spec:
  targetRef:
    name: my-app
  updatePolicy:
    updateMode: "Auto"
  resourcePolicy:
    containerPolicies:
    - containerName: app
      controlledResources: ["cpu", "memory"]
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
spec:
  metrics:
  - type: Pods
    pods:
      metric:
        name: http_requests_per_second  # Custom metric, not CPU
      target:
        type: AverageValue
        averageValue: "100"
```

## Common Pitfalls

- **Flapping (rapid scale-up/scale-down oscillation)**: Traffic spikes cause HPA to scale up aggressively, then immediately scale down when spike ends. Fix: Set `behavior.scaleDown.stabilizationWindowSeconds: 300` and conservative scale-down policies. [src5]
- **Metrics lag during startup**: New pods report high CPU during initialization, causing HPA to scale up further. Fix: Kubernetes ignores CPU for pods in the CPU initialization period (default 5 minutes). Set appropriate readiness probes. [src1]
- **Resource requests too low**: If `requests.cpu: 50m` but the app actually needs 200m idle, HPA sees 400% utilization and scales to 8x more pods than needed. Fix: Use VPA recommendations to set accurate resource requests first. [src6]
- **Custom metrics not found**: HPA shows `FailedGetPodsMetric` error. Fix: Verify prometheus-adapter is running (`kubectl get apiservice v1beta1.custom.metrics.k8s.io`) and metrics are exposed (`kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1"`). [src3]
- **minReplicas: 1 in production**: Single pod means zero redundancy during scaling lag. Fix: Set `minReplicas: 2` minimum for production workloads to maintain availability during pod failures. [src6]
- **Ignoring the 10% tolerance**: HPA has a default 0.1 tolerance -- ratios within 0.9-1.1 do not trigger scaling. This means a 50% CPU target won't scale until usage exceeds ~55% or drops below ~45%. Fix: Account for this when setting targets. [src1]
- **Multiple metrics select highest replica count**: When specifying CPU and custom metrics, HPA picks the metric requesting the most replicas. This can cause over-provisioning. Fix: Tune each metric target independently and test with realistic load. [src1]

## Diagnostic Commands

```bash
# Check if metrics-server is running
kubectl get deployment metrics-server -n kube-system

# View current resource usage for pods
kubectl top pods -n <namespace>

# View HPA status (current metrics vs targets)
kubectl get hpa -n <namespace>

# Detailed HPA status with events and conditions
kubectl describe hpa <hpa-name> -n <namespace>

# Check HPA YAML including computed values
kubectl get hpa <hpa-name> -o yaml

# Verify custom metrics API is registered
kubectl get apiservice | grep metrics

# List available custom metrics
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1" | jq '.resources[].name'

# List available external metrics
kubectl get --raw "/apis/external.metrics.k8s.io/v1beta1" | jq '.resources[].name'

# Check specific custom metric value for a pod
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/default/pods/*/http_requests_per_second" | jq .

# Watch HPA scaling events in real time
kubectl get events --field-selector reason=SuccessfulRescale --watch

# Check KEDA ScaledObject status
kubectl get scaledobject -n <namespace>
kubectl describe scaledobject <name> -n <namespace>
```

## Version History & Compatibility

| API Version | K8s Version | Status | Key Changes |
|---|---|---|---|
| autoscaling/v2 | 1.23+ (GA) | Current, recommended | Multiple metrics, custom/external metrics, scaling behavior, container resources |
| autoscaling/v2beta2 | 1.12-1.25 | Removed in 1.26 | Identical to v2; migrate to autoscaling/v2 |
| autoscaling/v2beta1 | 1.8-1.25 | Removed in 1.26 | Early multi-metric support |
| autoscaling/v1 | 1.2+ | Supported but limited | CPU-only, no scaling behavior, no custom metrics |
| Container resources | 1.20 (beta), 1.27 (GA) | Current | Per-container metric targeting |
| KEDA | 2.x | Current (CNCF graduated) | 60+ scalers, scale-to-zero, ScaledObject/ScaledJob |

## When to Use / When Not to Use

| Use When | Don't Use When | Use Instead |
|---|---|---|
| Stateless web services need to handle variable HTTP traffic | Workload is stateful with complex state management (databases) | Manual scaling or operator-based scaling |
| CPU/memory utilization correlates with load | Load pattern is purely event-driven (queue consumers, cron jobs) | KEDA with event-source triggers |
| Need to scale replicas 1-N based on resource metrics | Need to scale from 0 to N (cold-start workloads) | KEDA (supports scale-to-zero) |
| Prometheus already collects application metrics in-cluster | Only cloud provider metrics available (no in-cluster Prometheus) | Cloud-native solutions (GKE MPA, AWS KEDA) or external metrics adapter |
| Need standard, built-in Kubernetes autoscaling | Need to right-size pod resource requests/limits | Vertical Pod Autoscaler (VPA) |
| Multiple metrics should drive scaling decisions | Single-event trigger with complex business logic | Custom controller or KEDA with multiple triggers |

## Important Caveats

- The HPA scaling formula `desiredReplicas = ceil(currentReplicas * (currentMetric / desiredMetric))` operates on average metrics across all ready pods -- a single hot pod does not trigger scaling unless the average exceeds the target
- HPA evaluates metrics every 15 seconds by default, but scaling decisions are further smoothed by the stabilization window -- expect 30-60 seconds minimum response time for scale-up
- When multiple metrics are specified, HPA calculates desired replicas for each metric independently and selects the maximum -- this can lead to over-provisioning if metrics are not well-correlated
- Container resource metrics (targeting a specific container instead of the whole pod) are GA since Kubernetes 1.27 -- use this when sidecar containers skew whole-pod CPU averages
- KEDA does not replace HPA; it creates and manages HPA objects under the hood -- KEDA's ScaledObject generates an HPA for scaling from 1-N, and handles the 0-1 transition itself
- Cluster Autoscaler is complementary to HPA: HPA scales pods, then Cluster Autoscaler scales nodes when pods cannot be scheduled due to insufficient resources

## Related Units
<!-- Generated from related_kos frontmatter -->

- [Kubernetes Metrics Server Setup](/software/devops/k8s-metrics-server/2026)
- [Kubernetes Resource Requests and Limits](/software/devops/k8s-resource-management/2026)
- [Prometheus Monitoring Setup](/software/devops/prometheus-monitoring/2026)
- [KEDA Event-Driven Autoscaler](/software/devops/keda-autoscaler/2026)
- [Kubernetes Vertical Pod Autoscaler](/software/devops/k8s-vpa/2026)
- [Cluster Autoscaler](/software/devops/cluster-autoscaler/2026)
