Self-hosted service
zalando-incubator/kube-metrics-adapter avatar
zalando-incubator/kube-metrics-adapter

kube-metrics-adapter: custom metrics for Kubernetes Horizontal Pod Autoscaling

General purpose metrics adapter for Kubernetes HPA metrics

542 stars116 forksGoMIT

At a glance

What is it?
kube-metrics-adapter is a Zalando incubator project that serves custom and external metrics to the Kubernetes Horizontal Pod Autoscaler, collecting them from Prometheus, SQS queues, JSON endpoints on individual pods and other sources. Collectors start on demand so the adapter only gathers the metrics a cluster actually scales on.
Who is it for?
Adopt kube-metrics-adapter if you need to scale on a signal the built-in resource metrics do not cover, such as queue depth or requests per second, since it implements the custom metrics API server and starts collectors only for the metrics your HPAs request. Do not adopt it if CPU and memory scaling is enough, because the standard resource metrics API covers that without another component in the cluster.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the adapter adds to the autoscaler

Kube Metrics Adapter is a general purpose metrics adapter for Kubernetes that can collect and serve custom and external metrics for Horizontal Pod Autoscaling.

The problem it solves is specific. The Horizontal Pod Autoscaler normally scales on CPU and memory usage, and Kubernetes also exposes an API for custom and external metrics so that other signals can drive scaling. That API is an extension point, and something has to sit behind it and answer questions like what is the current value of requests-per-second across the pods of this deployment. This adapter is that something.

The README names the sources it supports out of the box: Prometheus metrics, SQS queues and others. The dependency list backs that up, with the Prometheus client library and the Prometheus common package, the AWS SDK v2 with its config and SQS service packages, an InfluxDB client, an Argo Rollouts dependency for the Rollout scale target, a route group client, and a cluster-lifecycle-manager dependency.

The implementation detail that matters most is stated plainly: it discovers Horizontal Pod Autoscaling resources and starts to collect the requested metrics and stores them in memory. It is implemented using the custom-metrics-apiserver library from the Kubernetes SIGs, which is the standard way to write an API server extension that plugs into the aggregation layer.

The discovery behaviour is what makes this practical. The adapter does not poll every metric source in the cluster on a timer. It watches for HPA resources, reads what each one asks for, and starts a collector for exactly that metric. In a cluster with a dozen HPAs using a dozen different signals, the adapter runs a dozen small collectors rather than one broad scraper.

The primary language is Go, the licence is MIT, the default branch is master, and the recent releases are v0.2.9 from 2026-04-30, v0.2.8 from 2026-02-20 and v0.2.7 from 2026-01-20.

How the metric-config annotations wire a collector to an HPA

The configuration model is the part to understand first, because it is how a signal gets defined without a separate config file.

Collectors are different implementations for getting metrics requested by an HPA resource, and they are configured either simply based on the metrics defined in an HPA resource, or via additional annotations on the HPA resource.

The annotation namespace is metric-config, and the shape is metric-config.<metricType>.<metricName>.<collectorType>/<configKey>. In that scheme, metricType is Pods, metricName is the name of the metric as it appears in the HPA spec, collectorType names the implementation, and configKey is one of the collector's options.

Here is the README's example, an HPA that scales a deployment named myapp on a requests-per-second metric.

yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: myapp-hpa
  annotations:
    # metric-config.<metricType>.<metricName>.<collectorType>/<configKey>
    metric-config.pods.requests-per-second.json-path/json-key: "$.http_server.rps"
    metric-config.pods.requests-per-second.json-path/path: /metrics
    metric-config.pods.requests-per-second.json-path/port: "9090"
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: myapp
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Pods
    pods:
      metric:
        name: requests-per-second
      target:
        averageValue: 1k
        type: AverageValue

The annotations configure a json-path pod collector, and the metric itself is declared in the spec with a type of Pods and a target of AverageValue against an averageValue of 1k.

The important consequence of the scheme is that the annotation lives on the HPA resource. That means the scaling configuration and the metric-source configuration are the same object, which is convenient when a deployment is owned by the same team as its HPA, and awkward when the metrics are defined centrally by a platform team and the HPA is owned by an application team. There is no other configuration surface described in the README, so that is the whole story.

Because the annotations are keys rather than a nested structure, the metric name and the collector name are baked into the key. Renaming a metric in the spec means renaming it in three annotation keys too.

The pod collector and its json-path configuration

The pod collector allows collecting metrics from each pod matching the label selector defined in the HPA's scaleTargetRef, and currently only json-path collection is supported.

The scaleTargetRef handling is what identifies which pods to talk to. The pod collector uses the scaleTargetRef specified in an HPA resource to obtain the label selector from the referenced Kubernetes object, which enables the identification and management of pods associated with that object. The supported Kubernetes objects are Deployment, StatefulSet and Rollout, the last being the Argo Rollouts resource.

The metric type table lists custom as the only option: no predefined metrics, metrics are generated from user defined queries, of type Pods, available on Kubernetes versions >=1.12.

The full json-path configuration is richer than the first example. The README's second HPA shows the complete set of options.

yaml
    metric-config.pods.requests-per-second.json-path/json-key: "$.http_server.rps"
    metric-config.pods.requests-per-second.json-path/json-eval: "ceil($['active processes'] / $['total processes'] * 100)" # cannot use both json-eval and json-key
    metric-config.pods.requests-per-second.json-path/path: /metrics
    metric-config.pods.requests-per-second.json-path/port: "9090"
    metric-config.pods.requests-per-second.json-path/scheme: "https"
    metric-config.pods.requests-per-second.json-path/aggregator: "max"
    metric-config.pods.requests-per-second.json-path/interval: "60s" # optional
    metric-config.pods.requests-per-second.json-path/min-pod-ready-age: "30s" # optional

The option semantics are worth reading in the README's own order. json-key defines the json-path query for extracting the metric, and it assumes the pod exposes metrics in JSON format, so the expected payload for the first example is this.

json
{
  "http_server": {
    "rps": 0.5
  }
}

The json-eval option allows for more complex calculations on the extracted metric, evaluated using ajson's script engine, and the comment in the example warns that you cannot use both json-eval and json-key.

The path, port and scheme options specify where the metrics endpoint is exposed on the pod. The path and port options do not have default values, so they must be defined, while the scheme is optional and defaults to http.

The aggregator option specifies the aggregation function used to aggregate values of JSONPath expressions that evaluate to arrays or slices of numbers. It is optional, but when the expression does evaluate to an array, its absence produces an error. The supported functions are avg, max, min and sum.

The json-path query support depends on the github.com/spyzhov/ajson library, and it is expected that the metric you query returns something that can be turned into a float64.

Building it and installing it into a cluster

The build is a Go modules project, so the toolchain requirement is Go 1.11 or newer, and with Go 1.11 itself you also need to activate module support.

sh
export GO111MODULE=on # needed if the project is checked out in your $GOPATH.
$ make

The install into a cluster is a kubectl apply of the manifests in the docs directory.

sh
$ cd kube-metrics-adapter/docs
$ kubectl apply -f .

The Makefile shows how the project is actually built and packaged. The default target is build.local, and there are also build.linux, build.osx, build.docker and build.push targets. The image name defaults to ghcr.io/zalando-incubator/kube-metrics-adapter and the tag is derived from git describe --tags --always --dirty, so a dirty working tree produces a marked image.

The Dockerfile is minimal and distroless. The base image is gcr.io/distroless/static-debian12:latest, the binary is added from build/linux/${TARGETARCH}, and the entrypoint is the kube-metrics-adapter binary itself. No shell, no package manager, nothing to patch.

The Makefile also carries the code generation pipeline, which is the interesting part of the build. The deepcopy functions are generated by hack/update-codegen.sh, the CRD YAML files are produced with go tool controller-gen and then moved into docs/ with the expected filenames, and the OpenAPI code is generated with go tool openapi-gen from the Kubernetes custom metrics API packages.

That generation is not incidental. The Makefile also references the Zalando CRD API group at pkg/apis/zalando.org, with a types file and a generated deepcopy file, and a CRD_TYPE_SOURCE variable pointing at it. So the adapter ships its own custom resource definition for Zalando's scaling schedules, which is a Zalando-specific extension on top of the general purpose adapter.

Testing is go test with a coverage profile, and the check target runs go mod download followed by golangci-lint with a two minute timeout. The project also carries a .golangci.yml configuration and a CODE_OF_CONDUCT.md.

Kubernetes version compatibility and the API requirement

The compatibility policy is stated in the same terms Kubernetes uses for its own components, which is the right reference point for an API server extension.

Like the support policy offered for Kubernetes, this project aims to support the latest three minor releases of Kubernetes.

There is also a hard requirement. The default supported API is autoscaling/v2, available since v1.23, and this API MUST be available in the cluster which is the default.

That is worth pausing on, because the HPA API has been through several revisions. autoscaling/v2 is the version with the metrics list, and the example manifests in the README use it throughout with apiVersion autoscaling/v2. If you have a cluster still on autoscaling/v2beta1 or v2beta2 for its HPAs, this adapter's examples will not apply directly.

The Go module dependencies also tell you what the project is built against. The Kubernetes libraries are at 0.37.x, including k8s.io/api, apimachinery, apiserver, client-go, component-base, klog and metrics, and the custom metrics API server is sigs.k8s.io/custom-metrics-apiserver at v1.37.0.

There are two other things in the repository worth knowing about before you evaluate it. There is a SECURITY.md, which for a component that runs inside a cluster and reads HPA resources is worth reading, since it describes how the project wants vulnerability reports handled. And there is a MAINTAINERS file alongside CONTRIBUTORS.md and CONTRIBUTING.md, plus a delivery.yaml that presumably describes how releases are delivered.

The docs directory is where the install manifests live, and the example/ directory holds a separate worked example with its own Dockerfile, Makefile, deploy/ directory and main.go, which is the fastest way to see the adapter configured end to end.

The how-to/ directory and the hack/ directory round out the tree, with hack holding the code generation scripts that the Makefile drives.

Where the adapter is the wrong choice

There are three limits, and the first is that it does nothing for you if the standard metrics suffice.

The first is scope. If CPU and memory based scaling is enough for your workload, the built-in resource metrics API already covers it and adding this component to the cluster buys nothing. The adapter earns its place only when there is a signal that is genuinely better for scaling decisions than CPU usage, and that is a narrower set of cases than it sounds. Queue depth on an SQS queue is the obvious one, because a worker pool that is saturated looks identical to a worker pool that is idle until requests start timing out.

The second is that per-pod metric collection is a distributed systems problem you are now owning. The json-path collector has to reach every pod matching the label selector, on the port and path you specified, with the scheme you specified, and parse whatever that pod returns. If a pod is restarting, or has no metrics endpoint, or returns something that is not the JSON you expected, the collector has to decide what to do, and the failure mode is a scaling decision based on a subset of pods. The min-pod-ready-age option exists precisely because a newly started pod reports a misleading zero, and if a workload does not expose a JSON metrics endpoint at all, the collector simply cannot use it.

The third is that the configuration model puts the burden of consistency on the annotation keys. The metric name appears in three places in an HPA manifest: in the spec, in the json-path annotation, and in the query itself. A mismatch produces a metric that never resolves, and the symptom is an HPA that sits at its minimum replica count with a message about missing metrics rather than a clear configuration error. With one HPA this is fine. With the same pattern copied across thirty deployments, you want some generator or policy in front of it.

There is also the Zalando-specific surface to weigh. The dependency on cluster-lifecycle-manager and the generated CRDs for Zalando's scaling schedules mean part of this codebase serves an internal platform's needs, and the way it is built and released may reflect that. The Makefile's release tooling and the MAINTAINERS file suggest a specific operational model rather than a community one.

The honest alternative is to not use a metrics adapter at all and scale on queue length or request rate through the mechanism your platform already provides, such as KEDA, which turns a queue or a Prometheus query into a scaled workload without you writing HPA manifests and annotation keys by hand. The difference in approach is that a metrics adapter exposes raw metric values through the Kubernetes custom metrics API and leaves the scaling policy in the HPA, where the policy stays declarative and reviewable. A scale-to-zero capable platform skips the HPA entirely and owns the loop itself, which is simpler to operate and harder to express in Kubernetes-native terms.

If you want to keep everything inside the Kubernetes API and you want the scaling decisions visible as HPA objects, this adapter is a reasonable way to get there, and the Prometheus and SQS collectors save you from writing your own.

Editorial conclusion

Adopt kube-metrics-adapter if you need to scale on a signal the built-in resource metrics do not cover, such as queue depth or requests per second, since it implements the custom metrics API server and starts collectors only for the metrics your HPAs request. Do not adopt it if CPU and memory scaling is enough, because the standard resource metrics API covers that without another component in the cluster. Before you install, confirm your cluster has autoscaling/v2 available, since the README says that API must be present, and note the last push was on 2026-10-19 with v0.2.9 from 2026-04-30.

Frequently asked questions

What is kube-metrics-adapter and what does it do?

It is a general purpose metrics adapter for Kubernetes that collects and serves custom and external metrics for Horizontal Pod Autoscaling. It discovers HPA resources, starts to collect the requested metrics and stores them in memory, and it is implemented using the custom-metrics-apiserver library.

How do I configure a custom metric for the kube-metrics-adapter?

Through annotations on the HPA resource, using the shape metric-config.<metricType>.<metricName>.<collectorType>/<configKey>. For a pod metric from a JSON endpoint, the collector is json-path and the options include json-key or json-eval, path, port, scheme, aggregator, interval and min-pod-ready-age.

Which Kubernetes versions does kube-metrics-adapter support?

Like the Kubernetes version skew policy, the project aims to support the latest three minor releases of Kubernetes. The default supported API is autoscaling/v2, available since v1.23, and the README states this API must be available in the cluster.

How do I install kube-metrics-adapter into a Kubernetes cluster?

Clone the repository and apply the manifests in the docs directory with kubectl apply -f . from kube-metrics-adapter/docs. The image is built from the repository Dockerfile on a distroless base, and the Makefile provides build.linux, build.osx, build.docker and build.push targets with the image defaulting to ghcr.io/zalando-incubator/kube-metrics-adapter.

What metric sources does kube-metrics-adapter support?

The README states that it supports scaling based on Prometheus metrics, SQS queues and others out of the box. The pod collector currently supports only json-path collection, from pods matching the label selector of the HPA's scaleTargetRef, where the supported objects are Deployment, StatefulSet and Argo Rollout.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. zalando-incubator/kube-metrics-adapter on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/zalando-incubator-kube-metrics-adapter.svg)](https://hysenlabs.com/projects/zalando-incubator-kube-metrics-adapter)

Was this useful?