> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Demo Cost Visibility with OpenCost

> Demonstrates using OpenCost to map Kubernetes resource requests to real time dollar costs, identify overprovisioned workloads, and guide rightsizing to reduce cluster expenses.

Many platform engineers are surprised to discover their Kubernetes clusters are wasting money — not because workloads aren't running, but because they are overprovisioned. Teams commonly request resources far above actual usage (for example, requesting `2` CPUs while using `50m`), and when you multiply that across dozens of deployments, the bill grows quickly.

The root cause is visibility. Without per-resource cost allocation you can't easily tell which team or workload is responsible for spend. OpenCost maps Kubernetes resource requests and usage to dollar cost in real time — per namespace, deployment, and pod — so you can identify and act on cost drivers.

In this lesson we examine a sample stack and show how OpenCost reveals where the cost comes from and how simple changes can reduce it.

OpenCost runs in its own namespace and relies on a Prometheus stack for metrics. Inspect OpenCost and Prometheus pods:

```console theme={null}
controlplane ~ ➜ kubectl get pods -n opencost
NAME                          READY   STATUS    RESTARTS         AGE
opencost-5b676b669f-pc9hk     2/2     Running   3 (77m ago)      78m

controlplane ~ ➜ kubectl get pods -n prometheus-system
NAME                                                     READY   STATUS    RESTARTS   AGE
prometheus-alertmanager-0                                 1/1     Running   0          78m
prometheus-kube-state-metrics-978b59b4c-cnzq8             1/1     Running   0          78m
prometheus-prometheus-node-exporter-4q2fp                 1/1     Running   0          78m
prometheus-prometheus-pushgateway-75b8bf7c77-sgpvv        1/1     Running   0          78m
prometheus-server-86854bc5fb-qbmn5                        2/2     Running   0          78m
```

For a complete cross-namespace view:

```console theme={null}
controlplane ~ ➜ kubectl get pods -A
NAMESPACE              NAME                                                     READY   STATUS    RESTARTS         AGE
kube-flannel           kube-flannel-ds-2jbpk                                    1/1     Running   0                100m
kube-system            coredns-6f6c7df987-58jrf                                 1/1     Running   0                100m
kube-system            coredns-6f6c7df987-v5vrg                                 1/1     Running   0                100m
kube-system            etcd-controlplane                                         1/1     Running   0                100m
kube-system            kube-apiserver-controlplane                               1/1     Running   0                100m
kube-system            kube-controller-manager-controlplane                      1/1     Running   0                100m
kube-system            kube-proxy-xkswm                                          1/1     Running   0                100m
kube-system            kube-scheduler-controlplane                               1/1     Running   0                100m
local-path-storage     local-path-provisioner-7d4d469b6-kg2mz                    1/1     Running   0                80m
opencost               opencost-5b676b669f-pc9hk                                 2/2     Running   3 (78m ago)     80m
prometheus-system      prometheus-alertmanager-0                                 1/1     Running   0                80m
prometheus-system      prometheus-kube-state-metrics-978b59b4c-cnzq8             1/1     Running   0                80m
prometheus-system      prometheus-prometheus-node-exporter-4q2fp                 1/1     Running   0                80m
prometheus-system      prometheus-prometheus-pushgateway-75b8bf7c77-sgpvv        1/1     Running   0                80m
prometheus-system      prometheus-server-86854bc5fb-qbmn5                        2/2     Running   0                80m
team-backend           api-d5cc567999-9kbgq                                      1/1     Running   0                80m
team-backend           api-d5cc567999-gppdt                                      1/1     Running   0                79m
team-data              cache-586f5ccdf6-2m2sk                                    1/1     Running   0                80m
team-frontend          web-55fddc9fcf-499v8                                      1/1     Running   0                32m
team-frontend          web-55fddc9fcf-7hvm6                                      1/1     Running   0                79m
team-frontend          web-55fddc9fcf-bn1zm                                      1/1     Running   0                32m
team-frontend          web-55fddc9fcf-h7hqn                                      1/1     Running   0                32m
team-frontend          web-55fddc9fcf-lwz4q                                      1/1     Running   0                32m
```

OpenCost's dashboard (and API) provides a visual and programmatic breakdown of cluster cost by CPU, memory, and storage. You can drill down by namespace, deployment, and pod. In this example cluster we have three application namespaces with workloads: `team-frontend`, `team-backend`, and `team-data`.

[Kubernetes](https://kubernetes.io/) scheduling and cost allocation are typically based on resource requests. Over-requesting CPU and memory directly increases your accounted cost, even if actual runtime usage is low.

Let's inspect each deployment to see the requested resources that drive cost.

* team-frontend (`web`) — five replicas
* team-backend (`api`) — two replicas
* team-data (`cache`) — one replica

Summary: requested resources and total impact

| Deployment | Namespace | Replicas | Request (CPU / pod) | Request (Memory / pod) | Total CPU requested | Total Memory requested |
| - | - | -: | -: | -: | -: | -: |
| `web` | `team-frontend` | 5 | `500m` | `512Mi` | `2.5` CPU (5 × `500m`) | `2560Mi` (≈ `2.5Gi`) |
| `api` | `team-backend` | 2 | `100m` | `128Mi` | `0.2` CPU (2 × `100m`) | `256Mi` |
| `cache` | `team-data` | 1 | `250m` | `1Gi` | `0.25` CPU | `1Gi` |

Describe the `web` deployment in `team-frontend`:

```console theme={null}
controlplane ~ ➜ kubectl describe deploy -n team-frontend web
Name:                   web
Namespace:              team-frontend
CreationTimestamp:      Sun, 05 Apr 2026 15:24:57 +0000
Labels:                 app=web
Annotations:            deployment.kubernetes.io/revision: 2
Selector:               app=web
Replicas:               5 desired | 5 updated | 5 total | 5 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  app=web
  Containers:
   nginx:
    Image:        nginx
    Port:         <none>
    Host Port:    <none>
    Limits:
      cpu:     1
      memory:  1Gi
    Requests:
      cpu:     500m
      memory:  512Mi
    Environment:   <none>
    Mounts:        <none>
  Volumes:        <none>
  Node-Selectors: <none>
  Tolerations:    <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Progressing    True    NewReplicaSetAvailable
  Available      True    MinimumReplicasAvailable
OldReplicaSets:  web-68d995574f (0/0 replicas created)
NewReplicaSet:   web-55fdcc9fcf (5/5 replicas created)
Events:
  Type    Reason              Age   From                   Message
  ----    ------              ----  ----                   -------
  Normal  ScalingReplicaSet   38m   deployment-controller  Scaled down replica set web-55fdcc9fcf from 5 to 1
  Normal  ScalingReplicaSet   33m   deployment-controller  Scaled up replica set web-55fdcc9fcf from 1 to 5
```

Each web pod requests `500m` CPU and `512Mi` memory. With five replicas, total requested CPU is `2.5` cores — this is why `team-frontend` appears as the most expensive namespace.

Describe the `api` deployment in `team-backend`:

```console theme={null}
controlplane ~ ➜ kubectl describe deploy -n team-backend api
Name:           api
Namespace:      team-backend
CreationTimestamp: Sun, 05 Apr 2026 15:24:57 +0000
Labels:         app=api
Annotations:    deployment.kubernetes.io/revision: 2
Selector:       app=api
Replicas:       2 desired | 2 updated | 2 total | 2 available | 0 unavailable
StrategyType:   RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
  Labels:  app=api
  Containers:
    nginx:
      Image:        nginx
      Port:         <none>
      Host Port:    <none>
      Limits:
        cpu:     200m
        memory:  256Mi
      Requests:
        cpu:     100m
        memory:  128Mi
      Environment:  <none>
      Mounts:       <none>
  Volumes:        <none>
  Node-Selectors: <none>
  Tolerations:    <none>
Conditions:
  Type        Status  Reason
  ----        ------  ------
  Available   True    MinimumReplicasAvailable
  Progressing True    NewReplicaSetAvailable
OldReplicaSets: api-7db8db7947 (0/0 replicas created)
NewReplicaSet: api-d5cc56799 (2/2 replicas created)
Events: <none>
```

Each API pod requests `100m` CPU and `128Mi` memory. With two replicas, total requested CPU is `200m` (`0.2` CPU).

Describe the `cache` deployment in `team-data`:

```console theme={null}
controlplane ~ ➜ kubectl describe deploy -n team-data cache
Name:                   cache
Namespace:              team-data
CreationTimestamp:      Sun, 05 Apr 2026 15:24:57 +0000
Labels:                 app=cache
Annotations:            deployment.kubernetes.io/revision: 2
Selector:               app=cache
Replicas:               1 desired | 1 updated | 1 total | 1 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  app=cache
  Containers:
    redis:
      Image:        redis
      Port:         <none>
      Host Port:    <none>
      Limits:
        cpu:       500m
        memory:    2Gi
      Requests:
        cpu:       250m
        memory:    1Gi
      Environment:  <none>
      Mounts:       <none>
  Volumes:         <none>
  Node-Selectors:  <none>
  Tolerations:     <none>
Conditions:
  Type        Status  Reason
  ----        ------  ------
  Progressing True    NewReplicaSetAvailable
  Available   True    MinimumReplicasAvailable
OldReplicaSets:  cache-77c88869b5 (0/0 replicas created)
NewReplicaSet:   cache-586f5ccdf6 (1/1 replicas created)
Events:
  Type    Reason              Age   From                  Message
  ----    ------              ----  ----                  -------
  Normal  ScalingReplicaSet   38m   deployment-controller  Scaled up replica set cache-586f5ccdf6 from 1 to 2
  Normal  ScalingReplicaSet   34m   deployment-controller  Scaled down replica set cache-586f5ccdf6 from 2 to 1
```

The cache pod requests `250m` CPU and `1Gi` memory (limits `500m` / `2Gi`).

Why is team-frontend the most expensive?
Because cost allocation is typically based on requested resources. Five frontend replicas each requesting `500m` yields `2.5` CPU total, which translates directly into higher cost compared to backend (`0.2` CPU) and data (`0.25` CPU).

If you need reliability (for example for a database), it's reasonable to request more resources. But for stateless frontends, you can often right-size requests and replicas.

Before changing replica counts or resource requests in production, validate performance and run load tests. Rightsizing without testing can affect availability.

Optimizing the cluster in this example is straightforward. For instance, scale down the frontend and scale up the cache:

```console theme={null}
controlplane ~ ➜ kubectl scale deployment web -n team-frontend --replicas=1
deployment.apps/web scaled

controlplane ~ ➜ kubectl scale deployment cache -n team-data --replicas=2
deployment.apps/cache scaled
```

After making these changes, refresh the OpenCost UI. As the new pods start and Prometheus collects fresh metrics, the cost allocation will reflect the updated requested resources and namespace-level costs should normalize.

You can also query OpenCost's API directly for automation. The example below requests a one-hour window aggregated by namespace and formats selected fields with `jq`:

```bash theme={null}
controlplane ~ ➜ curl -sG "http://localhost:30904/allocation/compute?window=1h&aggregate=namespace" | \
jq '
(.data[0] // {})
| to_entries[]
| {
  namespace: .key,
  cpuCost: .value.cpuCost,
  ramCost: .value.ramCost
}
'
```

Example output (one JSON object per namespace):

```json theme={null}
{
  "namespace": "kube-flannel",
  "cpuCost": 0.00271,
  "ramCost": 0.00018
}
```

```json theme={null}
{
  "namespace": "kube-system",
  "cpuCost": 0.02307,
  "ramCost": 0.00085
}
```

```json theme={null}
{
  "namespace": "local-path-storage",
  "cpuCost": 0,
  "ramCost": 0
}
```

```json theme={null}
{
  "namespace": "opencost",
  "cpuCost": 0.00054,
  "ramCost": 0.00039
}
```

```json theme={null}
{
  "namespace": "prometheus-system",
  "cpuCost": 0,
  "ramCost": 0
}
```

```json theme={null}
{
  "namespace": "team-backend",
  "cpuCost": 0.00543,
  "ramCost": 0.00091
}
```

```json theme={null}
{
  "namespace": "team-data",
  "cpuCost": 0.00764,
  "ramCost": 0.0041
}
```

```json theme={null}
{
  "namespace": "team-frontend",
  "cpuCost": 0.05045,
  "ramCost": 0.00676
}
```

The API outputs the same allocation data you see in the GUI and can be used to automate reports or integrate cost checks into CI/CD pipelines and governance workflows.

That was our demo on OpenCost. Use the UI for quick exploration and the API for automation.

Links and references

* OpenCost — [https://www.opencost.io/](https://www.opencost.io/)
* OpenCost API docs — [https://docs.opencost.io/api/](https://docs.opencost.io/api/)
* OpenCost docs — [https://docs.opencost.io/](https://docs.opencost.io/)
* Prometheus — [https://prometheus.io/](https://prometheus.io/)
* Kubernetes — [https://kubernetes.io/](https://kubernetes.io/)
* jq — [https://stedolan.github.io/jq/](https://stedolan.github.io/jq/)

> **[Watch Video](https://learn.kodekloud.com/user/courses/prep-course-certified-cloud-native-platform-engineer-cnpe/module/989346de-0207-4837-af11-bf456d188972/lesson/4dfb9257-7cc3-4837-b314-207a8d758584)**

> **[Practice Lab](https://learn.kodekloud.com/user/courses/prep-course-certified-cloud-native-platform-engineer-cnpe/module/989346de-0207-4837-af11-bf456d188972/lesson/9d957351-7ded-4b48-a240-cd70699a17e1)**


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.