Self-hosted service
kubernetes-sigs/custom-metrics-apiserver avatar
kubernetes-sigs/custom-metrics-apiserver

custom-metrics-apiserver is the boilerplate behind custom metrics adapters

Framework for implementing custom metrics support for Kubernetes

530 stars179 forksGoApache-2.0

At a glance

What is it?
The Kubernetes custom metrics apiserver boilerplate is a library, not a server: you implement a metrics provider interface, hand it to AdapterBase, and get a working custom and external metrics API server with the aggregation and serving layers already done.
Who is it for?
custom-metrics-apiserver fits a team that needs a custom metrics adapter to expose an application specific number to a Horizontal Pod Autoscaler or to a monitoring stack, and that would rather implement one provider interface than assemble an aggregated API server from scratch.
Can I use it commercially?
Yes. Apache-2.0 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 9 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 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

It is a library, not a server you can deploy as is

The name on the repository describes an implementation of the Kubernetes Metrics APIs, and the README says plainly that it contains boilerplate code for setting up such an implementation. The two APIs covered are custom metrics, from the k8s.io/metrics/pkg/apis/custom_metrics package, and external metrics, from the k8s.io/metrics/pkg/apis/external_metrics package.

The key point for anyone deciding whether to use it is that this repository is designed to be used as a library. You do not clone it and run a binary. You implement one or more of the metrics provider interfaces in pkg/provider, for example the CustomMetricsProvider interface, depending on which of the two APIs you want to support. You then use the AdapterBase in pkg/cmd to initialise the necessary flags and set up the API server, passing in your providers.

That division is the whole design. The parts of running a metrics adapter that every implementation needs, the generic API server setup and the registration of resources, are here, and the part that differs, how you compute a metric for a given object, is yours. The repository also includes an implementation for testing that allows setting custom metric values over HTTP.

There is a getting started guide in the docs directory, and the test adapter lives in the test-adapter directory.

Custom metrics and external metrics are different questions

The two API groups in the Kubernetes metrics repository are easy to confuse and the distinction decides what you build.

Custom metrics are attached to Kubernetes objects. A request names a resource, for example a service or a pod, and asks for a metric about that object, and the value is a quantity in the resource's own units, a count or a rate or a fraction of capacity. The test adapter's write path in the README uses exactly that shape, a value like 300m for a service named kubernetes, which is a quantity rather than a plain number.

External metrics are not attached to anything in the cluster. The request names a metric and the adapter answers, typically for a queue depth or a latency of an external service, and the value is a number in units the adapter defines.

The reason this matters for the code you write is that the provider interfaces are separate. The README says to implement one or more of the metrics provider interfaces in pkg/provider depending on which APIs you want to support, so an adapter that serves only custom metrics does not need to answer external metric queries, and the aggregation and API wiring follows from which providers you pass to AdapterBase.

Both API groups are served by the same process in the typical case, since the framework takes a list of providers.

The test adapter lets you set a metric over HTTP

The test adapter in this repository exists for three purposes at once: testing changes to the repository, serving as a mock implementation of the APIs for automated unit tests, and acting as an example implementation. The README is emphatic about the limit, that it should not be used for production, because it is for writing automated end to end tests and serving as a sample only.

What makes it useful as a sample is that you can define new metrics on it by querying the write endpoint. The write paths match the same URL structure as the read paths, but at the /write-metrics base path, and the data has to be in JSON with the content type header set accordingly:

bash
kubectl proxy &
# write a sample metric -- the write paths match the same URL structure
# as the read paths, but at the /write-metrics base path.
# data needs to be in json, so we also need to set the content-type header
curl -X POST \
  -H 'Content-Type: application/json' \
  http://localhost:8001/api/v1/namespaces/custom-metrics/services/custom-metrics-apiserver:http/proxy/write-metrics/namespaces/default/services/kubernetes/test-metric \
  --data-raw '"300m"'

The kubectl proxy is what makes the write endpoint reachable, since the write paths are not exposed by the API server aggregation in the usual way.

Once the deployment is ready, the values are read back through the standard API paths, for custom metrics at the custom.metrics.k8s.io group and for external metrics at the external.metrics.k8s.io group, both of which the README shows with the raw get command and an optional pipe to jq.

Driving it from a Horizontal Pod Autoscaler

The reason anyone builds a custom metrics adapter is usually the autoscaler, and the README walks through the exact case: target a simple nginx deployment and use the metric as an HPA scaler metric.

The manifest is a standard autoscaling/v2 HorizontalPodAutoscaler with an Object metric, which names the metric, describes the object it is measured against, and sets a target value. The described object here is a Service named kubernetes, which is the service the test adapter reported a value for:

yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: nginx-deployment
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: nginx-deployment
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Object
    object:
      metric:
        name: test-metric
      describedObject:
        apiVersion: v1
        kind: Service
        name: kubernetes
      target:
        type: Value
        value: 300m

The quantity format in the target, 300m, is worth pausing on. It is the same value written to the adapter, and it is the format the custom metrics API uses for anything that is a fraction or a rate rather than a plain count.

For a real adapter this is where your provider earns its keep. The autoscaler asks for a metric for a described object, your provider returns a quantity, and the autoscaler scales on it. Everything between, the API server plumbing, the aggregation, the quantity handling, is what this boilerplate provides.

Building and testing the adapter locally

The Makefile drives the test adapter, and the build produces a container image named after the registry you set. The registry is an environment variable in the example, and the target is make test-adapter-container:

bash
# build the test-adapter container as $REGISTRY/k8s-test-metrics-adapter-amd64
export REGISTRY=<some-prefix>
make test-adapter-container

For local testing the README points at kind, the Kubernetes cluster in Docker, and the flow is to create a cluster and run the tests:

bash
kind create cluster
make test-kind

For a real cluster, the container is pushed to a registry and the adapter is launched with the deployment manifest in test-adapter-deploy:

bash
# push the container up to a registry
docker push $REGISTRY/k8s-test-metrics-adapter-amd64

# launch the adapter using the test adapter deployment manifest
kubectl apply -f test-adapter-deploy/testing-adapter.yaml

The Makefile also carries the OpenAPI generation rules, with the core, custom metrics and external metrics packages each generated from the corresponding Kubernetes API packages by the openapi-gen tool, and the generated files written into pkg/generated/openapi.

The build pins a linter version, 2.12.2, and the repository has a .golangci.yml to match, which is the kind of detail that tells you the project keeps its own tooling current rather than letting it drift.

The Go version and the API libraries travel together

The prerequisite section of the README is one line and it is strict: the library requires the same version of Go as Kubernetes. The link is to the Kubernetes contributor development documentation, which is where the current Go version for a given Kubernetes release is recorded.

That requirement is not pedantry. The module file shows the whole Kubernetes dependency set pinned to the same minor version, 0.37.1, across k8s.io/api, k8s.io/apimachinery, k8s.io/apiserver, k8s.io/client-go, k8s.io/component-base and k8s.io/metrics, and the module itself declares Go 1.26.0 with a toolchain directive for go1.26.4.

The direct dependencies are few and each has a job. The REST framework is go-restful, the flag parsing is pflag, the logging is klog, the Kubernetes API types come from the api and apimachinery modules, the server machinery from apiserver, the client from client-go, and the metrics API types from the metrics module. The OpenAPI generation is handled by kube-openapi.

The compatibility note in the README is that the APIs in this repository follow the standard guarantees for Kubernetes APIs and will follow Kubernetes releases, which is the promise you want from a component that sits inside a cluster: the version you build against is tied to the cluster version you run it in, not to a separate release cadence of its own.

Where this is the wrong tool

The first case is obvious but worth stating: if the metric you want is already a standard resource metric, this buys you nothing. CPU and memory usage are what the cluster already collects, and the horizontal pod autoscaler can scale on them without any adapter at all. Building a custom metrics adapter is only justified when the number you need is not in the default set.

The second is the operational weight. What this framework produces is an aggregated API server inside your cluster, which means certificates, an aggregation layer configuration, a Deployment, and the usual questions about who watches it and what happens when it is down. The framework removes the code, not the operational surface, and a team without a platform group should weigh that carefully.

The third is the Go version coupling. A library that requires the same Go version as Kubernetes ties your adapter's build to the cluster release cadence, and that is usually fine but is not free when you run a mixed cluster or an unusual build pipeline.

The fourth is the test adapter. It is explicitly not for production, and the reason is obvious once you look at how it works, since anyone who can reach its write endpoint can set any metric value to any number, and a metrics API that accepts values on request is a metrics API that will happily scale your workloads on fiction.

Licence, community and the shape of the project

The project is licensed under Apache-2.0, with the LICENSE file at the root of the repository, and it lives under the kubernetes-sigs organisation with the module path sigs.k8s.io/custom-metrics-apiserver, so it imports like any other Kubernetes ecosystem library.

The repository layout matches the structure of the project. The pkg directory holds the provider interfaces and the cmd package with AdapterBase, plus the generated OpenAPI code. The test-adapter directory holds the sample implementation and test-adapter-deploy holds the manifest used to launch it. The hack directory holds the boilerplate used by the code generators, and the docs directory holds the getting started guide. There is also a Makefile, an OWNERS file, a RELEASE.md, a SECURITY_CONTACTS file, a code-of-conduct.md and a CONTRIBUTING.md.

The release history is where the compatibility promise shows up in practice. The recent versions are v1.37.0 on 2026-10-14, v1.36.0 on 2026-06-15 and v1.35.0 on 2026-02-25, so the major version tracks the Kubernetes release it targets, and the last push to the repository was on 2026-10-14 with the repository not archived.

Support goes through the standard Kubernetes channels, the Slack channel for the instrumentation special interest group and the mailing list for the same group, and participation is governed by the Kubernetes code of conduct.

Editorial conclusion

custom-metrics-apiserver fits a team that needs a custom metrics adapter to expose an application specific number to a Horizontal Pod Autoscaler or to a monitoring stack, and that would rather implement one provider interface than assemble an aggregated API server from scratch. It is the wrong tool if you only need the standard resource metrics, which the cluster already provides, and the wrong tool if you need an in-tree Kubernetes change, since this is a library you build and deploy yourself. Before you start, check three things: that your Go version matches the version Kubernetes itself uses, which the library requires explicitly, that you can serve the API with TLS and the right aggregation rules, since the framework expects to run as a real API server in the cluster, and that the metric you want is expressible as a value the adapter can compute per object. The code is Apache-2.0, the current release is v1.37.0 from 2026-10-14, and the last push was on 2026-10-14.

Frequently asked questions

What does the custom metrics apiserver boilerplate give me?

It gives you the generic API server setup and resource registration for the custom and external metrics APIs, plus the AdapterBase in pkg/cmd. You implement the metrics provider interfaces in pkg/provider and pass your providers to AdapterBase, and the framework handles the serving layer.

How do I run the test adapter locally?

Install kind, then run `kind create cluster` followed by `make test-kind`. To build the container first, set REGISTRY and run `make test-adapter-container`.

How do I set a metric value on the test adapter?

Start a proxy with `kubectl proxy &` and post to the write path, which mirrors the read URL structure under the /write-metrics base path, with a JSON content type header and a value such as "300m".

Can I scale a deployment with a custom metric from this adapter?

Yes, with an autoscaling/v2 HorizontalPodAutoscaler using an Object metric that names the metric, the described object and a target value in quantity format such as 300m, as the README shows for an nginx deployment.

What Go version does custom-metrics-apiserver require?

The same version of Go as Kubernetes, according to the README. The module declares Go 1.26.0 with a go1.26.4 toolchain directive, and the Kubernetes API libraries are pinned to 0.37.1.

Official sources

  1. Issues
  2. kubernetes-sigs/custom-metrics-apiserver on GitHub
  3. License: Apache-2.0
  4. README
  5. Releases
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/kubernetes-sigs-custom-metrics-apiserver.svg)](https://hysenlabs.com/projects/kubernetes-sigs-custom-metrics-apiserver)