- Python 72.8%
- Dockerfile 24.1%
- Shell 3.1%
| microcontroller-wol-agent | ||
| node-agent | ||
| operator | ||
| wol-agent | ||
| README.md | ||
k8s-power-manager
A Kubernetes operator for managing power on bare-metal nodes that have no BMC. Nodes are shut down and woken via Wake-on-LAN when needed. The idea is that it can be used in conjuction with something like kube-downscaler to help keep our electricity bills down
Current Status: not working! Right now the overall architecture is in place, but I will get it working as and when family/home life allows
Overview
┌─────────────────────────────────────────────┐
│ Kubernetes Cluster │
│ │
│ ┌─────────────┐ ┌────────────────┐ │
│ │ operator │───────▶│ node-agent │ │
│ │ (kopf) │ │ (DaemonSet) │ │
│ └──────┬──────┘ └────────────────┘ │
│ │ │
└─────────┼───────────────────────────────────┘
│ HTTP /wake
▼
┌─────────────┐ ─ ─ ─ ─ ─ ─ ─
│ wol-agent │──WOL──▶ bare-metal │
│ (Pi Zero) │ ─ ─ ─ ─ ─ ─ ─
└─────────────┘
The operator:
- Cordons and drains the managed node on a cron schedule
- Calls the
node-agentto triggershutdown -h now - At wake time, calls the
wol-agentto send a WOL magic packet - Waits for the node to become
Ready, then scales workloads back up
Components
wol-agent
A small Flask HTTP server that sends Wake-on-LAN magic packets. Runs on a device physically attached to the same L2 network as the managed nodes (required for broadcast).
Supported platforms:
| Platform | Runtime |
|---|---|
| Raspberry Pi Zero W / Zero 2 W | Docker (armv6l, armv7l, amd64) |
| ESP32 | MicroPython |
| Raspberry Pi Pico W | MicroPython |
Endpoint: POST /wake?mac=AA:BB:CC:DD:EE:FF
node-agent
A Flask HTTP server that triggers host shutdown. Runs as a DaemonSet, only on nodes labelled power-manager/managed=true.
Platforms: arm64, amd64
Security:
- Shared secret via
X-Agent-Secretheader - Distroless-style image (shells removed) to prevent
kubectl execabuse NetworkPolicyrestricting access to the operator pod only
Endpoint: POST /shutdown
operator
A kopf-based Python operator that reconciles PowerManagedNode CRDs.
CRD
apiVersion: power.homelab/v1alpha1
kind: PowerManagedNode
metadata:
name: heavy-node-1
spec:
nodeName: heavy-node-1
macAddress: "aa:bb:cc:dd:ee:ff"
wolAgent: "http://pi-zero.local:8080/wake"
sleepSchedule: "0 23 * * *" # shut down at 23:00
wakeSchedule: "0 7 * * *" # wake at 07:00
managedWorkloads:
- kind: Deployment
name: ollama
namespace: ai
Node phases: Running → Draining → Off → Waking → WaitingForReady → Running
Getting Started
Deploy the wol-agent
cd wol-agent
cp .env.example .env # set WOL_BROADCAST to your subnet e.g. 192.168.1.255
docker compose up -d
For MicroPython devices, edit WIFI_SSID and WIFI_PASSWORD in wol-agent-micropython/main.py, then:
mpremote mip install microdot
mpremote cp wol-agent-micropython/main.py :main.py
Build multi-arch images
docker buildx create --use --name multiplatform
# wol-agent (includes armv6l for Pi Zero W)
docker buildx build \
--platform linux/arm/v6,linux/arm/v7,linux/amd64 \
--tag yourrepo/wol-agent:latest --push \
./wol-agent
# node-agent
docker buildx build \
--platform linux/arm64,linux/amd64 \
--tag yourrepo/node-agent:latest --push \
./node-agent
#operator
docker buildx build \
--platform linux/arm64,linux/amd64 \
--tag yourrepo/node-agent:latest --push \
./operator
Deploy the operator
kubectl apply -f operator/crd/powermanagednode.yaml
kubectl apply -f operator/manifests/
Repository Structure
.
├── wol-agent/ # Pi Zero / SBC Docker agent
├── wol-agent-micropython/ # ESP32 / Pico W MicroPython agent
├── node-agent/ # In-cluster shutdown DaemonSet
└── operator/ # kopf operator