Configuration
Configuration
Section titled “Configuration”The values that change what Reactor observes or how it behaves. Every value the chart takes, with the note written above it in values.yaml, is in the Chart values reference:
| Value | Default | Description |
|---|---|---|
crds.install | true | install and upgrade the Automation CRD with the release |
crds.adopt | true | on the first upgrade from chart 0.3.0 or earlier, take the CRD that packaging left owned by no release into this one |
unifi.url | — | UniFi console base URL; the provider stays disabled until this is set |
unifi.site | default | UniFi Network site |
unifi.pollInterval | 30s | how often WAN, internet and UPS state are observed |
unifi.maxObservationAge | "" | how old the observed state may get before every automation reports ObservationStale and says so. Empty is unbounded — and silent (above) |
unifi.insecureSkipVerify | true | accept the console’s self-signed certificate |
unifi.existingSecret | unifi-reactor-credentials | Secret holding UNIFI_API_KEY; re-read on every poll, so rotating the key needs no restart |
log.level | info | debug adds the per-observation lines used to work out why an automation did not fire |
unifi.ups.lowBatteryPercent | 30 | charge at or below this reports ups.battery: low |
unifi.ups.criticalBatteryPercent | 10 | charge at or below this reports ups.battery: critical |
unifi.ups.shortRuntimeSeconds | 600 | remaining runtime at or below this reports ups.runtime: short |
unifi.ups.criticalRuntimeSeconds | 180 | remaining runtime at or below this reports ups.runtime: critical |
unifi.ups.highLoadPercent | 80 | draw at or above this share of the power budget reports ups.load: high |
unifi.wan.quality.minAvailabilityPercent | 99 | availability below this reports wan.quality: degraded |
unifi.wan.quality.maxLatencyMs | 150 | average latency above this reports wan.quality: degraded |
unifi.temperature.highCelsius | 75 | hottest adopted device at or above this reports temperature: high |
unifi.poe.maxUtilizationPercent | 90 | a switch delivering at or above this share of its PoE budget reports poe: insufficient |
unifi.devices.perDeviceKeys | false | also publish a device.<name> key per adopted device — one more series per device |
unifi.webhook.enabled | false | webhook fast path (below) |
actions.allowedDestinations | [] | where outbound actions may go. Empty refuses all of them, and withholds the operator’s read access to Secrets (why) |
metrics.enabled | false | serve /metrics on :8443 over HTTPS behind the API server’s authn/authz filter (above) |
metrics.serviceMonitor.enabled | false | scrape it with the Prometheus Operator |
metrics.rules.enabled | false | ship the alert rules, ReactorObservationStale first |
metrics.dashboard.enabled | false | ship the overview dashboard as a grafana-operator GrafanaDashboard |
rbac.clusterWide | true | when false, restricts the operator to its own namespace |
safety.dryRun | false | evaluate and report everything, write nothing, and withhold the permissions that could (above) |
safety.detectHPA | false | notice a HorizontalPodAutoscaler driving a target and decline it rather than fight (above) |
Automation resources are namespaced. An action targets its own namespace by default; naming a different one in target.namespace requires rbac.clusterWide: true.
Log level
Section titled “Log level”log.level takes debug, info (the default), error, or a V-level number. debug turns on the per-observation lines — what each poll saw, and why a transition did or did not happen — which is what you want when an automation did not fire:
helm upgrade reactor oci://ghcr.io/robbeverhelst/charts/reactor \ --namespace reactor-system --reuse-values --set log.level=debuglog.format: json switches the encoder for a log collector.
Charts up to 0.3.0 hardcoded the manager’s arguments and ran at debug; the default is now info. --set log.level=debug restores the previous output.
Rotating the UniFi API key
Section titled “Rotating the UniFi API key”The key is mounted from unifi.existingSecret and re-read on every poll, so rotation takes effect on its own — no restart, no second controller, nothing for anyone to remember:
kubectl -n reactor-system create secret generic unifi-reactor-credentials \ --from-literal=UNIFI_API_KEY=<new key> \ --dry-run=client -o yaml | kubectl -n reactor-system apply -f -The kubelet refreshes the mounted file within its sync period (about a minute by default) and the next poll authenticates with the new key. Revoke the old key in the UniFi UI once you see polling continue. If the file is ever unreadable or empty, that poll fails and is logged; the next one retries.
If you would rather have the pod restart on change — because you already run reloader, say — annotate the Deployment instead:
annotations: secret.reloader.stakater.com/reload: unifi-reactor-credentialsPodDisruptionBudget
Section titled “PodDisruptionBudget”Off by default, because with one replica a budget cannot protect anything: minAvailable: 1 turns a node drain into a hang. Run two replicas and enable it — leader election keeps exactly one instance acting, so the second is a warm standby:
replicaCount: 2podDisruptionBudget: enabled: true minAvailable: 1