Skip to content

Prometheus Adapter for HPA on EKS

First PublishedByAtif Alam

Use prometheus-adapter when Horizontal Pod Autoscaler (HPA) should scale on application metrics (request rate, queue lag, business KPIs) defined in PromQL, not only CPU and memory.

Overview: Autoscaling on EKS. k3s lab: Prometheus Adapter HPA on k3s. For CloudWatch-native metrics, see Container Insights for HPA on EKS.

Use Prometheus AdapterConsider KEDA instead
RPS, latency, in-cluster PromQLSQS / Kafka / cron triggers
You already run Prometheus or AMP remote writeMinimal metrics plumbing for queues
Same metric feeds dashboards and HPAEvent source is outside Prometheus
┌─────────────────────────────┐
│ prometheus-adapter │
│ (custom.metrics.k8s.io) │
└──────────────┬──────────────┘
│ queries
┌───────────────────────┼───────────────────────┐
▼ ▼ ▼
In-cluster Prometheus Amazon Managed Prometheus Remote Prometheus URL
(Helm / operator) (workspace + remote write) (VPC endpoint / VPN)
BackendThis guide
In-cluster Prometheus (e.g. kube-prometheus-stack)Primary walkthrough below
Amazon Managed Prometheus (AMP)Alternative setup — AMP

You still need metrics-server if the same HPA mixes Resource metrics (CPU) with custom metrics.

  • EKS cluster 1.23+, kubectl configured.
  • Helm 3Helm.
  • Prometheus reachable from the adapter pod (in-cluster Service DNS or AMP query endpoint).
  • Cluster admin to install APIService and ClusterRole for the adapter.

Verify APIs:

Terminal window
kubectl get apiservice v1beta1.metrics.k8s.io
# After adapter install:
kubectl get apiservice v1beta1.custom.metrics.k8s.io

Install prometheus-adapter (in-cluster Prometheus)

Section titled “Install prometheus-adapter (in-cluster Prometheus)”

Assume Prometheus from kube-prometheus-stack in namespace monitoring, Service monitoring-kube-prometheus-prometheus on port 9090. Adjust names to your release.

Create adapter-rules.yaml:

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>>)'

Create prometheus-adapter-values.yaml:

prometheus:
url: http://monitoring-kube-prometheus-prometheus.monitoring.svc
port: 9090
rules:
existing: adapter-rules
replicas: 2
resources:
requests:
cpu: 100m
memory: 128Mi
Terminal window
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm upgrade --install prometheus-adapter prometheus-community/prometheus-adapter \
--namespace monitoring \
--create-namespace \
-f prometheus-adapter-values.yaml \
--wait
Terminal window
kubectl get pods -n monitoring -l app.kubernetes.io/name=prometheus-adapter
kubectl get apiservice v1beta1.custom.metrics.k8s.io
# List custom metrics (names vary with your rules)
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1" | head -c 2000

Expected: adapter pods Running; APIService Available; raw API returns resource kinds.

Terminal window
kubectl logs -n monitoring -l app.kubernetes.io/name=prometheus-adapter --tail=100

Deploy a workload that exports http_requests_total and is scraped by Prometheus ( ServiceMonitor with release: monitoring if using kube-prometheus-stack — see Observability setup).

Example HPA fragment (metric name must match adapter rule output, e.g. http_requests_per_second):

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: sample-app-hpa
namespace: default
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: sample-app
minReplicas: 2
maxReplicas: 10
metrics:
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: "100"
Terminal window
kubectl apply -f sample-app-hpa.yaml
kubectl describe hpa sample-app-hpa -n default
  • Networking: Prometheus in another VPC or AMP private endpoint requires security groups and DNS so adapter pods can query port 9090 / 443.
  • IRSA: Adapter pods usually need no AWS API access; workloads scraping AWS metrics may — keep IAM on the app or use Container Insights guide instead.
  • AMP: Use workspace query URL in prometheus.url / prometheus.port and ensure adapter identity can reach the endpoint (VPC endpoints, IAM if using SigV4 proxy).
  • High availability: Run 2+ adapter replicas; APIService aggregates from any healthy adapter.

Alternative setup: Amazon Managed Prometheus

Section titled “Alternative setup: Amazon Managed Prometheus”
  1. Create an AMP workspace and configure remote write from in-cluster Prometheus or ADOT collectors.
  2. Point prometheus-adapter prometheus.url at the AMP query endpoint for your workspace (see current AMP documentation).
  3. Reuse the same rules ConfigMap; validate metric names exist in AMP with the AMP query editor.
  4. Install adapter via Helm as above with updated prometheus-adapter-values.yaml.

AMP networking and authentication change with private VPC endpoints — validate kubectl get --raw from a debug pod before wiring HPA.

SymptomLikely causeAction
APIService not AvailableAdapter crash, TLS, wrong Prometheus URLCheck adapter logs and prometheus.url
HPA unable to get metricsMetric name mismatchCompare describe hpa with kubectl get --raw .../namespaces/NS/...
Metric never appearsNo series in PrometheusTargets UP? PromQL returns data?
Forbidden on metrics APIRBACAdapter ClusterRole / APIService aggregation
Works in Prometheus, not HPARule seriesQuery / metricsQuery wrongTest PromQL; fix adapter rules
Terminal window
kubectl describe hpa <name> -n <namespace>
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/<namespace>/pods/*/http_requests_per_second"

For on-call commands, see EKS troubleshooting cheat sheet — HPA.