Power- and thermal-aware workload control for single-node MicroShift on battery-powered, thermally-constrained, or intermittently-powered edge devices.
When battery or thermal headroom drops, microshift-power gracefully sheds
workloads via the Kubernetes Eviction API while protecting what matters.
Lightweight by design — targets 20m CPU / 32Mi RAM idle so it stays out of the way on the same hardware whose power it's protecting.
- Battery and thermal aware
- Hysteresis state machine
- Pod priority classes
- Per-pod escape hatches
- PDB-respecting evictions
- Optional admission webhook
- Protected system namespaces
- Prometheus metrics
- RPM + systemd add-on
- Beehive weight remote site
- Solar-powered telecom remote site (or meshcore repeaters)
- Vehicle-mounted compute
- Solar-powered agricultural sensor gateway
Each band has separate enterBelow / exitAbove thresholds; the gap is
the hysteresis band that prevents flapping at the threshold ±1 %.
| Key | Where | Effect |
|---|---|---|
power.microshift.io/priority |
pod | critical / standard / best-effort (overrides namespace default) |
power.microshift.io/min-battery-percent |
pod | Don't evict on battery grounds while battery ≥ N. Thermal eviction still wins. |
power.microshift.io/grace-period-seconds |
pod | Per-pod eviction grace period; 0 = immediate. |
power.microshift.io/default-priority |
namespace label | Default priority for pods in the namespace. |
apiVersion: v1
kind: Pod
metadata:
name: log-shipper
annotations:
power.microshift.io/priority: best-effort
spec:
containers: [...]Host config at /etc/microshift-power/config.yaml. Example below; the
state thresholds match the built-in defaults but the thermal.zones
list is illustrative — there are no default zones, and an omitted
thermal block leaves the thermal axis permanently Normal (the
controller relies on the battery axis alone). Configure zones for any
device you want to gate on temperature.
apiVersion: microshift-power/v1
kind: PolicyConfig
states:
conserve: { enterBelow: 60, exitAbove: 70 }
critical: { enterBelow: 30, exitAbove: 40 }
emergency: { enterBelow: 15, exitAbove: 25 }
classes:
best-effort: { evictInState: [Conserve, Critical, Emergency], blockAdmissionInState: [Critical, Emergency] }
standard: { evictInState: [Critical, Emergency], blockAdmissionInState: [Emergency] }
critical: { evictInState: [], blockAdmissionInState: [] }
thermal:
zones:
- { name: cpu-thermal, conserveAboveCelsius: 75, criticalAboveCelsius: 85, emergencyAboveCelsius: 95 }
defaultPriority: standard
admissionWebhook: { enabled: true }Protected namespaces (kube-*, openshift-*, microshift-*), DaemonSet
pods, and static pods are never evicted.
RPM + systemd, matching the MicroShift add-on convention
(microshift-gitops, microshift-multus,
microshift-ai-model-serving). The controller runs as a systemd
unit on the same node as MicroShift.
sudo dnf copr enable mmahut/microshift-power
sudo dnf install microshift-power
sudo systemctl enable --now microshift-power.service
sudo systemctl restart microshift.service # picks up manifests.d/
# Verify
systemctl status microshift-power
oc -n microshift-power get allThe RPM installs:
/usr/bin/microshift-power/usr/lib/systemd/system/microshift-power.service/etc/microshift-power/config.yaml(edit andsystemctl restart)/usr/lib/microshift/manifests.d/050-microshift-power/(Service + ValidatingWebhookConfiguration; MicroShift applies on next start)
Examples in examples/ cover four real-world scenarios: beehive weight remote site, telecom remote site, vehicle-mounted, and a solar-powered agricultural gateway using namespace-default labels.
Examples use
oc, the OpenShift CLI shipped with MicroShift.kubectlworks identically if you prefer it.
make build
# Real hardware (Linux)
./bin/microshift-power probe
# Synthetic fixture (any OS)
./bin/microshift-power probe \
--fake-file=pkg/power/fake/testdata/scenario-low-battery.yaml
