ByteGuideHub
Green Computing / Kubernetes • • 9 min read

Self-Hosted Energy-Aware Workload Scheduling with Kubernetes and Kepler

A practical guide to measuring and optimizing power consumption of containerized workloads using Kepler and integrating energy-aware scheduling policies in homelabs and edge clusters.

Sponsored / Ad Space

Self-Hosted Energy-Aware Workload Scheduling with Kubernetes and Kepler

Quick Summary (TL;DR) Kepler is a CNCF sandbox Prometheus exporter that uses eBPF plus hardware power models (Intel/AMD RAPL, GPU telemetry) to estimate per-pod energy in watts/joules. Deploy it as a DaemonSet, scrape the metrics with Prometheus, then feed them into a scheduler extender, descheduler, or policy engine to make energy-aware placement decisions. The payoff: lower power draw, better bin-packing, and a smaller carbon footprint — ideal for homelabs and edge clusters where every watt matters.

Introduction: The Need for Energy-Aware Kubernetes

Kubernetes is brilliant at scheduling for CPU, memory, and I/O — and almost blind to the one resource that actually costs you money every second of the day: electricity. A default scheduler will happily spread workloads across every node in the cluster, keeping idle machines powered on, spinning fans, and drawing baseline wattage for no reason.

In a cloud environment, that waste is abstracted behind a bill. In a homelab or edge deployment, it shows up on your electricity meter, in your rack’s thermals, and in the lifespan of your hardware. A rack of five nodes idling at 40 W each burns roughly 1.75 MWh per year doing nothing useful.

Energy-aware scheduling closes this gap. Instead of treating power as an invisible side effect, you measure it, expose it as a first-class metric, and schedule against it. The tool that makes this practical in Kubernetes is Kepler.

Understanding Kepler: Architecture and Metrics

Kepler (Kubernetes-based Efficient Power Level Exporter) is a CNCF sandbox project that estimates the energy consumption of individual containers. It does not require expensive power meters per node. Instead, it combines two signals:

  1. eBPF-based telemetry — tracking CPU cycles, cache misses, and I/O per cgroup to attribute resource usage to specific containers.
  2. Hardware power models — reading Intel/AMD RAPL (Running Average Power Limit) counters, NVIDIA GPU telemetry, and ACPI data to establish a total node power budget.

Kepler then proportionally attributes that node-level power to each pod based on its observed resource usage. The result is a per-container wattage estimate good enough for scheduling decisions.

flowchart LR
    A[Hardware Sensors<br/>RAPL / GPU / ACPI] --> C[Kepler DaemonSet]
    B[eBPF cgroup<br/>Counters] --> C
    C --> D[Prometheus<br/>Metrics Endpoint :9102]
    D --> E[Prometheus Server]
    E --> F[Grafana Dashboards]
    E --> G[Scheduler Extender /<br/>Descheduler / KubeGreen]
    G --> H[Kubernetes API<br/>Scheduling Decisions]

Key metrics exposed on /metrics:

MetricDescription
kepler_container_joules_totalCumulative energy per container (joules)
kepler_container_package_joules_totalCPU package energy per container
kepler_node_platform_joules_totalTotal node platform energy
kepler_container_cpu_wattsInstantaneous CPU power per container (watts)

Because energy is cumulative, use rate() in PromQL to get current wattage: rate(kepler_container_joules_total[1m]).

Prerequisites and Installation

Before installing Kepler, confirm your environment meets these requirements:

  • Linux kernel 4.18+ with BTF and eBPF enabled (CONFIG_DEBUG_INFO_BTF=y).
  • Hardware power sensors — Intel RAPL (intel_rapl module), AMD RAPL, or NVIDIA GPUs with DCGM.
  • Privileged access — Kepler needs CAP_SYS_ADMIN and host mounts for /sys and /proc.
  • Prometheus already deployed (or plan to install it).

Check kernel support:

uname -r
ls /sys/class/powercap/          # RAPL domains
grep CONFIG_DEBUG_INFO_BTF /boot/config-$(uname -r)

Note: Cloud VMs frequently virtualize away RAPL access. Kepler will still run but fall back to less accurate estimation models. Bare metal gives the best fidelity.

Deploying Kepler in Your Cluster

The fastest path is the official Helm chart:

helm repo add kepler https://sustainable-computing-io.github.io/kepler-helm-chart
helm repo update
helm install kepler kepler/kepler \
  --namespace kepler \
  --create-namespace \
  --set serviceMonitor.enabled=true

Verify the DaemonSet is running on every node:

kubectl get pods -n kepler -o wide
kubectl logs -n kepler daemonset/kepler --tail=20

You should see log lines confirming RAPL domains were detected and the exporter is listening on port 9102. Quick sanity check:

kubectl port-forward -n kepler svc/kepler 9102:9102
curl -s localhost:9102/metrics | grep kepler_container_joules_total | head

Collecting and Visualizing Power Metrics

Add a scrape job to your Prometheus config:

scrape_configs:
  - job_name: kepler
    kubernetes_sd_configs:
      - role: pod
    relabel_configs:
      - source_labels: [__meta_kubernetes_pod_label_app_kubernetes_io_name]
        regex: kepler
        action: keep
    metrics_path: /metrics
    scrape_interval: 15s

Then build a Grafana panel. Useful queries:

# Total cluster power (watts)
sum(rate(kepler_node_platform_joules_total[1m]))

# Power per namespace
sum by (namespace) (rate(kepler_container_joules_total[1m]))

# Top 5 power-hungry pods
topk(5, sum by (pod_name) (rate(kepler_container_joules_total[1m])))

The official Kepler Grafana dashboard (ID 16455 on grafana.com) gives you a ready-made starting point with per-node and per-namespace breakdowns.

Integrating Energy Metrics with Scheduling

Kubernetes’ default scheduler scores nodes on resource fit, affinity, and spread — not power. To make energy a scheduling input, you have three practical architectures:

ApproachMechanismComplexityBest For
Scheduler ExtenderHTTP webhook scores nodes using live Kepler dataHighProduction clusters needing custom logic
DeschedulerPeriodic rebalancing based on energy policiesMediumConsolidating idle workloads
Policy Engine (KubeGreen)CRD-driven scaling and placement rulesLowHomelabs, quick wins

The simplest effective pattern is a descheduler that evicts pods from underutilized nodes so they can be powered down or put to sleep.

sequenceDiagram
    participant D as Descheduler
    participant P as Prometheus (Kepler)
    participant K as Kube API
    D->>P: Query node power usage
    P-->>D: kepler_node_platform_joules_total
    D->>D: Identify node below threshold
    D->>K: Evict pods from idle node
    K-->>D: Pods rescheduled to busy nodes
    D->>K: Cordon idle node

Implementing Energy-Aware Scheduling Policies

Here’s a concrete, working policy using the Kubernetes descheduler with energy-driven eviction. First, identify idle nodes via PromQL, then annotate them for cordoning:

# Find nodes drawing less than 15W average
curl -s "http://prometheus:9090/api/v1/query" \
  --data-urlencode 'query=rate(kepler_node_platform_joules_total[5m]) < 15' \
  | jq -r '.data.result[].metric.node'

A minimal descheduler policy that drains these nodes:

apiVersion: descheduler/v1alpha2
kind: DeschedulerPolicy
profiles:
  - name: energy-aware
    pluginConfig:
      - name: LowNodeUtilization
        args:
          thresholds:
            cpu: 20
            memory: 20
            pods: 20
          targetThresholds:
            cpu: 70
            memory: 70
            pods: 70
    plugins:
      balance:
        enabled:
          - LowNodeUtilization

For true power-aware scoring, write a Scheduler Framework plugin that reads Kepler metrics and returns a node score inversely proportional to current wattage. Pseudocode:

func (p *EnergyPlugin) Score(ctx, state, pod, nodeName) (int64, *Status) {
    watts := p.prom.QueryWatts(nodeName)     // from Kepler
    maxWatts := p.prom.ClusterMaxWatts()
    // Prefer nodes already drawing power (bin-packing)
    score := int64(100 * (watts / maxWatts))
    return score, nil
}

This bin-packing bias concentrates workloads on fewer nodes, letting the rest idle or sleep.

Case Study: Homelab and Edge Scenarios

Homelab (3-node mini-PC cluster): A user running three NUC-class nodes found that Kepler revealed one node idling at 11 W while two others sat at 28 W. After enabling a bin-packing descheduler policy, they consolidated workloads onto two nodes and scheduled the third to suspend overnight via a cron-triggered systemctl suspend. Measured savings: ~0.5 kWh/day, roughly 180 kWh/year.

Edge (retail kiosk fleet): An edge deployment of 40 in-store nodes used Kepler to detect that ML inference pods were spread evenly, preventing any node from entering low-power states. By pinning inference to dedicated nodes with GPU acceleration and consolidating management pods, they cut average per-node draw by 18% and reduced thermal throttling incidents.

The lesson in both cases: visibility comes first. You cannot optimize what you cannot measure, and Kepler makes the invisible visible.

Best Practices and Optimization Tips

  • Scrape frequently but not aggressively. A 15s interval balances resolution against eBPF overhead.
  • Use rate() with a window matching your scrape interval to avoid noisy wattage readings.
  • Combine energy data with utilization. A node drawing 50 W at 90% utilization is efficient; one drawing 50 W at 5% is a consolidation target.
  • Set node power budgets as annotations and let policy engines consume them.
  • Test on staging first. Aggressive descheduling can cause churn and even increase power use through repeated pod restarts.
  • Pair with node sleep/wake automation (Wake-on-LAN, systemctl suspend) for the biggest wins in homelabs.
  • Watch for eBPF overhead on high-throughput nodes; monitor Kepler’s own CPU footprint.

Conclusion and Future Directions

Energy-aware scheduling is moving from novelty to necessity. As electricity costs rise and sustainability reporting becomes mandatory in more jurisdictions, understanding the watt-level cost of every workload will be as routine as tracking CPU and memory.

Kepler gives you the measurement layer today. The scheduling layer is maturing fast — expect tighter integration with the Kubernetes Scheduler Framework, standardized carbon-intensity signals (via projects like the Green Software Foundation’s Carbon Aware SDK), and native power-aware autoscaling in KEDA and HPA.

Start small: deploy Kepler, watch your dashboard for a week, and let the data tell you where your watts are going. The first consolidation policy you write will likely pay for the effort within a month.

FAQ

What is Kepler and how does it measure power consumption? Kepler (Kubernetes-based Efficient Power Level Exporter) is a Prometheus exporter that uses eBPF to monitor container-level resource usage and combines it with hardware power models (e.g., RAPL, GPU telemetry) to estimate energy consumption in watts. It exposes metrics like kepler_container_joules_total per container, pod, and namespace.

Can I use Kepler on any Kubernetes cluster? Kepler requires Linux kernel 4.18+ with eBPF support and access to hardware power sensors (e.g., Intel RAPL, AMD RAPL, or NVIDIA GPU metrics). It works on most bare-metal and VM clusters, but cloud instances may lack direct sensor access, limiting accuracy.

How do I integrate Kepler metrics with Kubernetes scheduling? You can use the Kepler metrics with a custom scheduler extender or a policy engine like KubeGreen. Alternatively, you can deploy a descheduler with energy-aware policies or use the Kubernetes Scheduler Framework to create a plugin that scores nodes based on their current power usage.

What are the benefits of energy-aware scheduling? Energy-aware scheduling can reduce overall power consumption by consolidating workloads onto fewer nodes, avoiding over-provisioning, and shifting workloads to nodes with better power efficiency. This leads to lower energy costs and a smaller carbon footprint, especially in homelabs and edge clusters.

Is Kepler production-ready? Kepler is actively developed by the Cloud Native Computing Foundation (CNCF) as a sandbox project. While it is used in production by some organizations, it is still evolving. For critical workloads, test thoroughly and consider it as part of a broader observability and optimization strategy.

How can I get started with energy-aware scheduling in a homelab? Start by deploying Kepler to measure your current power usage. Then, experiment with simple policies like bin-packing or node affinity based on power metrics. Tools like KubeGreen or custom scripts using the Kubernetes API can help automate scheduling decisions.

Sponsored / Ad Space