Report Kubernetes cluster and pod resource requests vs usage and generate static HTML
Go to file
pettersolberg88 7bd7435d7a fix: Harden deployment by dropping all capabilities and not allow privelege escalation (#51)
The change is to harden kube-resource-report deployment to drop all (unused) capabilities and not allow privelege escalation

Reviewed-on: #51
Co-authored-by: pettersolberg88 <>
Co-committed-by: pettersolberg88 <>
2023-02-09 20:52:06 +00:00
deploy fix: Harden deployment by dropping all capabilities and not allow privelege escalation (#51) 2023-02-09 20:52:06 +00:00
kube_resource_report update dependencies 2022-10-25 11:57:41 +02:00
sample-report Refactor and add pre-commit config (#142) 2020-04-10 13:06:08 +02:00
tests add e2e test with kind 2022-10-11 13:50:35 +02:00
unsupported/chart/kube-resource-report fix: new topology key for region, since previous key is deprecated (#47) 2022-10-12 11:50:23 +02:00
.flake8 update dependencies 2022-11-30 17:33:27 +01:00
.gitignore add e2e test with kind 2022-10-11 13:50:35 +02:00
.pre-commit-config.yaml update dependencies 2022-11-30 17:33:27 +01:00
.travis.yml poetry & black (#121) 2019-12-20 16:06:55 +01:00
Dockerfile update dependencies, multi-arch build 2022-07-12 13:58:18 +02:00
LICENSE add README 2018-02-07 19:35:51 +01:00
Makefile update dependencies 2022-11-30 17:33:27 +01:00 v22.11.0 2022-11-30 17:35:47 +01:00
poetry.lock update dependencies 2022-11-30 17:33:27 +01:00
pyproject.toml v22.11.0 2022-11-30 17:35:47 +01:00

Kubernetes Resource Report

Travis CI Build Status Code Coverage Docker pulls Calendar Versioning

⚠️ IMPORTANT SECURITY NOTICE: Kubernetes Resource Report does not provide any access control to the generated HTML report. You are responsible to properly secure your installation to not expose internal infrastructure details to the public internet!

This version only supports node costs for AWS EC2 (all regions, On Demand, Linux) and GKE/GCP machine types (all regions, On Demand, without sustained discount)

Script to generate a HTML report of CPU/memory requests vs. usage (collected via Metrics API/Heapster) for one or more Kubernetes clusters.

Want to see how the report looks? Check out the demo deployment!

What the script does:

  • Discover all clusters (either via ~/.kube/config, via in-cluster serviceAccount, or via custom Cluster Registry REST endpoint)
  • Collect all cluster nodes and their estimated costs (AWS and GCP only)
  • Collect all pods and use the application or app label as application ID
  • Get additional information for each app from the application registry (team_id and active field) OR use the team label on the pod
  • Group and aggregate resource usage and slack costs per cluster, team and application
  • Read and show VerticalPodAutoscaler (VPA) resource recommendations
  • Calculate own CPU/memory resource recommendations with a decaying exponential histogram
  • Allow custom links to existing systems (e.g. link to a monitoring dashboard for each cluster)

The primary goal of Kubernetes Resource Report is to help optimize Kubernetes resource requests and avoid slack. Slack is the difference between resource requests and resource usage/recommendation, e.g. requesting 2 GiB of memory and only using 200 MiB would mean 1.8 GiB of memory slack --- i.e. 1.8 GiB of memory capacity are blocked (and paid for), but unused.

Kubernetes Resource Report shows a Dollar value of potential savings, e.g. "You can potentially save 321.99 USD every month by optimizing resource requests and reducing slack". The potential savings are calculated by taking the cluster costs (sum of all node costs plus any additional configured costs) and attributing the relevant share per application/team by resource requests. Example: a cluster with 15 vCPUs capacity and 768 USD total costs runs an application with 1 vCPU slack, this would show as 51 USD potential savings for the application ("slack", disregarding memory in this example).


The usage requires Poetry (see below for alternative with Docker):

$ poetry install && poetry shell
$ mkdir output
$ python3 -m kube_resource_report output/ # uses clusters defined in ~/.kube/config
$ OAUTH2_ACCESS_TOKENS=read-only=mytok python3 -m kube_resource_report --cluster-registry= output/ # discover clusters via registry
$ OAUTH2_ACCESS_TOKENS=read-only=mytok python3 -m kube_resource_report --cluster-registry= output/ --application-registry= # get team information

The output will be HTML files plus multiple tab-separated files:


Main HTML overview page, links to all other HTML pages.


List of cluster summaries with number of nodes and overall costs.


List of potential savings (CPU/memory slack).


List of ingress host rules (informational).


List of routegroups host rules (informational).


List of all pods and their CPU/memory requests, usage, and recommendations.

Deploying to Minikube

This will deploy a single pod with kube-resource-report and nginx (to serve the static HTML):

$ minikube start
$ kubectl apply -f deploy/
$ kubectl port-forward service/kube-resource-report 8080:80

Now open http://localhost:8080/ in your browser.

Deploy using Helm Chart

IMPORTANT: Helm is not used by the maintainer of kube-resource-report - the Helm Chart was contributed by Eriks Zelenka and is not officially tested or supported!

Assuming that you have already helm properly configured (refer to helm docs), below command will install chart in the currently active Kubernetes cluster context.

This will deploy a single pod with kube-resource-report and nginx (to serve the static HTML):

$ git clone
$ cd kube-resource-report
$ helm install --name kube-resource-report ./unsupported/chart/kube-resource-report
$ helm status kube-resource-report

If you want to do upgrade, try something like:

$ cd kube-resource-report
$ git fetch --all
$ git checkout master & git pull
$ helm upgrade kube-resource-report ./unsupported/chart/kube-resource-report
$ helm status kube-resource-report

Use helm status command to verify deployment and obtain instructions to access kube-resource-report.

Running as Docker container

$ kubectl proxy & # start proxy to your cluster (e.g. Minikube)
$ # run kube-resource-report and generate static HTML to ./output
$ docker run --rm -it --user=$(id -u) --net=host -v $(pwd)/output:/output hjacobs/kube-resource-report:22.11.0 /output

For macOS:

$ kubectl proxy --accept-hosts '.*' & # start proxy to your cluster (e.g. Minikube)
$ # run kube-resource-report and generate static HTML to ./output
$ docker run --rm -it -e CLUSTERS=http://docker.for.mac.localhost:8001 --user=$(id -u) -v $(pwd)/output:/output hjacobs/kube-resource-report:22.11.0 /output

Application Registry

The optional application registry can provide information per application ID, it needs to have a REST API like:

$ curl -H 'Authorization: Bearer <mytok>'<application-id>
"team_id": "<team-id>",
"active": true

See the script in the sample-report folder for an example implementation.

The generated report can be enhanced with custom links to existing systems, e.g. to link to monitoring dashboards or similar. This currently works for clusters, teams, and applications. Custom links can be specified by providing the --links-file option which must point to a YAML file with the links per entity. Example file:

- href: "{name}"
  title: "Grafana dashboard for cluster {name}"
  icon: chart-area
- href: "{id}"
  title: "Grafana dashboard for application {id}"
  icon: chart-area
- href: "{id}"
  title: "Go to detail page of application {id}"
  icon: search
- href: "{id}"
  title: "Search team {id} on people.mycorp"
  icon: search
- href: "{cluster}/namespaces/{namespace}/ingresses/{name}"
  title: "View ingress {name} in Kubernetes Web View"
  icon: external-link-alt
- href: "{cluster}/namespaces/{namespace}/routegroups/{name}"
  title: "View routegroup {name} in Kubernetes Web View"
  icon: external-link-alt
- href: "{cluster}/namespaces/{namespace}/ingressroutes/{name}"
  title: "View ingressroute {name} in Kubernetes Web View"
  icon: external-link-alt
- href: "{cluster}/nodes/{name}"
  title: "View node {name} in Kubernetes Web View"
  icon: external-link-alt
- href: "{cluster}/namespaces/{name}"
  title: "View namespace {name} in Kubernetes Web View"
  icon: external-link-alt
- href: "{cluster}/namespaces/{namespace}/pods/{name}"
  title: "View pod {name} in Kubernetes Web View"
  icon: external-link-alt

For available icon names, see the Font Awesome gallery with free icons.

Custom Prices

Custom instance prices can be provided as CSV files in the following format:


The --pricing-file and --spot-pricing-file can be used to specify the file path to these custom prices. If --update-interval-minutes is provided the prices will be reloaded from the files each time the report is generated.

Customization Hooks

Kubernetes Resource Report allows customizing behavior by using Python hook functions. The following CLI options exist:

  • --prerender-hook: function to modify the HTML template context, e.g. to add arbitrary links. Example usage (built-in): --prerender-hook=kube_resource_report.example_hooks.prerender.
  • --map-node-hook: function to map Kubernetes Node objects and enrich them (e.g. with custom pricing). Example usage (built-in): --map-node-hook=kube_resource_report.example_hooks.map_node.
  • --map-pod-hook: function to map Kubernetes Pod objects and enrich them (e.g. applying a custom logic to set the application). Example usage (built-in): --map-pod-hooks=kube_resource_report.example_hooks.map_pod.

The hooks are Python functions which you need to define in a module (e.g. The module can either be added to the Dockerfile or mounted as a volume. Reference the functions via {module-name}.{function-name}, e.g. --map-pod-hook=hooks.map_pod if you defined the map_pod function in


You can run docker run --rm hjacobs/kube-resource-report:22.11.0 --help to find out information.

Besides this, you can also pass environment variables:

  • DEFAULT_CLUSTER_NAME (default: "cluster")
  • NODE_LABEL_SPOT (default: "")
  • NODE_LABEL_SPOT_VALUE (default: "true")
  • NODE_LABEL_ROLE (default: "")
  • NODE_LABEL_REGION (default: "")
  • NODE_LABEL_INSTANCE_TYPE (default: "")
  • OBJECT_LABEL_APPLICATION (default: "application,app,")
  • OBJECT_LABEL_COMPONENT (default: "component,")
  • OBJECT_LABEL_TEAM (default: "team,owner")