Green — energy & carbon
Teams are being asked for per-application energy and carbon numbers — ESRS E1 / CSRD reporting, internal sustainability targets, cost-of-compute pressure — and the usual answer is a spreadsheet estimate or a SaaS that wants the cluster's telemetry. avuru obs already knows which pod belongs to which service and what the CPU did; CNCF Kepler adds the one missing measurement (joules, from the CPU's RAPL counters), and the correlation the platform already does turns it into per-service Wh, per-request intensity, and gCO2e a report can cite — self-hosted, zero app changes, no external API.
What you get
- Per-service energy and carbon. Wh and gCO2e per service / deployment over
a window, on the
/greendashboard and as a gCO2e overlay on the service map you already watch. - Per-request intensity. Wh ÷ request count (gCO2e derived), plus a per-service trend — so an efficiency win shows up even as traffic grows.
- Carbon budgets. Monthly gCO2e budgets per service group, with warn at 80 %, exceeded at 100 %, and a month-end projection.
- A CSRD-ready export. Per-app numbers for ESRS E1 with a methodology block an auditor can follow.
How it works
Kepler measures per-pod CPU energy from RAPL and exposes it on a metrics
endpoint. It runs as an opt-in, probe-free fourth container in the sensor
DaemonSet (sensor.green.enabled); a scrape config carries its counters over
the existing OTLP → gateway path into the metric tables you already have.
The hub computes energy and carbon at query time:
Wh = Δjoules ÷ 3600 (per pod, summed to the workload)
gCO2e = Wh × grid-intensity × PUE
The pod→workload join reads the same kubeletstats attributes as node/pod health — which is why green requires the infra-metrics module. There are no new tables and no migration: it's the same read-time aggregation pattern as service and network health.
Grid intensity (gCO2e/kWh) and PUE are operator-set per cluster in config, with bundled per-country annual averages as defaults — no external API, no egress. Factor provenance (operator-set vs. bundled, and its vintage) is part of every export.
Budgets & the CSRD export
Budgets are declarative config (a hot-reloaded ConfigMap, like alerting rules), evaluated on the same tick as alerting and delivered through the channels you already configured. A budget fires once per crossing (warn / exceeded) and resolves on month rollover or when usage drops back. With the alerting module off, budgets still compute used / projected / ratio / status on the dashboard — only the notifications stop.
The export states how each number was produced: the formula, the grid factor and its provenance, the coverage ratio (attributed ÷ measured energy), and an explicit unattributed bucket for measured energy the join couldn't place. Numbers you can hand to an auditor and reproduce.
Coverage names its nodes. The /green coverage panel counts what it knows
— nodes known, measured, estimated, absent — and lists one row per node
with its Wh and the measured/estimated split kept visible per row; a node
reporting nothing shows at 0 Wh rather than being omitted, because a missing
row and a silent zero are the same picture otherwise.
A budget says whether it can reach anyone. Each budget resolves its own deliverability against the channels the evaluator actually walks — wired, alerting off, no channel set, or a channel that no longer exists — so the screen cannot promise a notification the tick would drop. A budget aimed at a service group nothing rolls up to is flagged too: it would otherwise evaluate forever at zero, which on a dashboard looks exactly like a quiet month.
Use cases
- Answer the CSRD / ESRS E1 ask without a spreadsheet. Per-service gCO2e with a stated methodology, exported on demand — no estimate, no third-party processor.
- Give each team a carbon budget. A monthly gCO2e ceiling per service group that warns before it's blown, on the alerting channels you already use.
- Prove an optimization worked. Per-request intensity isolates efficiency from traffic, so a code change that cut energy is visible even as load rises.
:::caution Confirm on real hardware before production Kepler's config keys, metric names/labels, port and RBAC are validated in CI against the pinned image's dev fake-cpu-meter, but are not yet confirmed against real RAPL hardware — do that before production use. The RAPL validation runbook walks every hop, from the node's powercap files to the numbers on the screen, and measures the estimator's error band against Kepler on the same node. The hub's Kepler metric names are configurable for exactly this reason. :::
Limitations
- Measured energy needs RAPL. Most public-cloud VMs expose no powercap/RAPL, so measured joules come from bare-metal / metal instances. Since v0.3 an opt-in TDP-based power model covers the rest — every number it produces is labeled estimated end to end and never blended with measured energy — and what is neither measured nor estimated is reported honestly as a coverage ratio rather than guessed at.
- Operational energy only. Scope 2-shaped CPU energy; no embodied / Scope 3 carbon.
- Static annual factors. Reported gCO2e uses annual-average grid intensity, not the grid's hour-by-hour reality; the methodology block says so.
- Per-service, not per-endpoint. Attribution is per service and per request, not per route.
- Off by default.
modules.green.enabledandsensor.green.enabledboth ship off — the signal is hardware-dependent, so an existing install upgrades unchanged.
:::note This page is expanding See the changelog entry, the Roadmap and Feature status. :::