A Kubernetes controller that scrapes Prometheus metrics directly from pods and manages HPAs to scale deployments based on those metrics.
Requires cert-manager for webhook TLS. The chart includes cert-manager as a subchart and installs it by default. If you already have cert-manager in your cluster, disable the subchart with --set cert-manager.enabled=false.
helm repo add epa https://ctxswitch.github.io/external-pod-autoscaler
helm upgrade --install epa epa/external-pod-autoscaler --namespace epa-system --create-namespaceCreate an ExternalPodAutoscaler resource:
apiVersion: ctx.sh/v1beta1
kind: ExternalPodAutoscaler
metadata:
name: my-app-scaler
namespace: default
spec:
minReplicas: 1
maxReplicas: 10
scrape:
port: 8080
interval: 15s
scaleTargetRef:
kind: Deployment
name: my-app
metrics:
- metricName: requests_total
targetValue: "100"The controller scrapes Prometheus metrics directly from pods, aggregates values over a sliding window, and creates a native HPA to scale the target deployment.
By default the controller scrapes metrics from the same workload it scales (scaleTargetRef). Set scrapeTargetRef to decouple them — scrape metrics from one workload while scaling another. This is useful for producer/consumer patterns where the scaling signal lives on a different service than the one being scaled:
spec:
scaleTargetRef:
kind: Deployment
name: consumer # workload to scale
scrapeTargetRef:
kind: Deployment
name: producer # workload to scrape metrics from
metrics:
- metricName: queue_depth
targetValue: "100"All duration fields accept humantime strings: 15s, 1m, 5m, 1h.
| Field | Type | Default | Description |
|---|---|---|---|
minReplicas |
int | 1 |
Minimum replica count |
maxReplicas |
int | required | Maximum replica count |
scrape |
ScrapeConfig | — | Metrics scraping configuration |
scaleTargetRef |
TargetRef | required | Workload to scale |
scrapeTargetRef |
TargetRef | (scaleTargetRef) | Workload to scrape metrics from. Defaults to scaleTargetRef if omitted. Set this to scrape a different workload than the one being scaled (e.g. a producer/consumer pattern). |
metrics |
[]MetricSpec | required | Metrics to collect and use for scaling decisions |
behavior |
Behavior | — | HPA scaling behavior (pass-through to HPA spec.behavior) |
| Field | Type | Default | Description |
|---|---|---|---|
port |
int | 8080 |
Port to scrape metrics from |
path |
string | /metrics |
HTTP path for the metrics endpoint |
interval |
duration | 15s |
How often to scrape each pod |
timeout |
duration | 1s |
Per-pod scrape timeout |
scheme |
string | http |
http or https |
evaluationPeriod |
duration | 60s |
Sliding window over which samples are aggregated |
aggregationType |
string | avg |
avg, max, min, median, last |
tls.insecureSkipVerify |
bool | false |
Skip TLS certificate verification (only applies when scheme: https) |
Used by both scaleTargetRef and scrapeTargetRef.
| Field | Type | Default | Description |
|---|---|---|---|
apiVersion |
string | apps/v1 |
API version of the target resource |
kind |
string | required | Deployment, StatefulSet, etc. |
name |
string | required | Name of the target resource |
| Field | Type | Default | Description |
|---|---|---|---|
metricName |
string | required | Prometheus metric name to scrape |
type |
string | AverageValue |
AverageValue (divided by replica count) or Value (raw) |
targetValue |
string | required | Scaling threshold |
aggregationType |
string | (from scrape) | Per-metric aggregation override |
evaluationPeriod |
duration | (from scrape) | Per-metric evaluation window override |
labelSelector |
LabelSelector | — | Filter scraped samples by Prometheus labels |
| Field | Type | Description |
|---|---|---|
matchLabels |
map[string]string | Key-value pairs, AND'd together |
matchExpressions[] |
list | Expression-based requirements, AND'd together |
matchExpressions[].key |
string | Label key |
matchExpressions[].operator |
string | In, NotIn, Exists, DoesNotExist |
matchExpressions[].values |
[]string | Required for In/NotIn |
Pass-through to the HPA behavior spec.
| Field | Type | Description |
|---|---|---|
scaleUp |
ScalingRules | Rules for scaling up |
scaleDown |
ScalingRules | Rules for scaling down |
scaleUp/scaleDown.stabilizationWindowSeconds |
int | Seconds to look back for flapping prevention |
scaleUp/scaleDown.selectPolicy |
string | Min, Max, or Disabled |
scaleUp/scaleDown.policies[] |
list | Scaling policies |
scaleUp/scaleDown.policies[].type |
string | Pods or Percent |
scaleUp/scaleDown.policies[].value |
int | Number of pods or percentage |
scaleUp/scaleDown.policies[].periodSeconds |
int | Time window for the policy |
Requires k3d.
make localdev # create k3d cluster + dev pod
make run # run controller in dev podA dummy-metrics service and EPA resource are provided for testing. In a separate terminal from the running controller:
make localdev-examplesThis deploys a dummy-metrics pod that exposes a configurable queue_depth metric on /metrics, and an ExternalPodAutoscaler that scrapes it and scales the dummy-metrics deployment between 1 and 10 replicas.
The metric value is driven by a ConfigMap. To simulate load and trigger scaling:
kubectl patch configmap dummy-metrics-value -n epa-system -p '{"data":{"value":"50"}}'Set the value back to "0" to scale down.
make build # cargo build --release
make test # cargo test
make clippy # cargo clippy
make fmt-check # cargo fmt --checkSee LICENSE file.