Deploy unpoller to scrape UniFi controller metrics and create VMRule alerts that detect link speed degradation. Configuration is self-contained via port naming convention in UniFi - no manual mappings required cluster-side.
In UniFi Network UI (https://192.168.1.1):
- Navigate to Settings → Admins & Users → Add Admin
- Create a local user:
- Username:
unpoller - Password: (generate secure password)
- Role: Viewer (read-only access)
- Username:
- Save the credentials for step 1.2
Create secrets at path /observability/unpoller/:
username=unpollerpassword= (the password from step 1.1)
kubernetes/apps/observability/unpoller/
├── ks.yaml # Flux Kustomization
├── kustomization.yaml # Kustomize resources
├── helmrelease.yaml # HelmRelease using official unpoller chart
└── externalsecret.yaml # Infisical credentials
ks.yaml - Flux Kustomization
spec.targetNamespace: observabilitydependsOn: victoria-metrics-k8s-stack(ensures scraping infrastructure exists)
externalsecret.yaml - Credentials from Infisical
- Pull
usernameandpasswordfrom/observability/unpoller/ - Target secret:
unpoller-secret
helmrelease.yaml - Official unpoller Helm chart
- Source:
oci://ghcr.io/unpoller/helm-chart/unpoller - Key configuration:
- UniFi controller URL:
https://192.168.1.1 - Prometheus enabled on port 9130
- InfluxDB disabled
- Credentials via environment variables from secret
- Security context: non-root, read-only filesystem
- Resources: minimal (50m CPU, 64Mi memory)
- UniFi controller URL:
kustomization.yaml - Resource list
- Include helmrelease.yaml and externalsecret.yaml
Add to kubernetes/apps/observability/kustomization.yaml:
- ./unpoller/ks.yamlkubernetes/apps/observability/vmrules/unifi-alerts.yaml
apiVersion: operator.victoriametrics.com/v1beta1
kind: VMRule
metadata:
name: unifi-alerts
spec:
groups:
- name: unifi-link-speed
interval: 30s
rules:
# Compliance: Catch unconfigured ports
- alert: UnifiPortMissingSpeedLabel
expr: unifi_port_speed_bps{port_name!~".+-(100M|1G|2\\.5G|10G)"} > 0
for: 10m
labels:
severity: warning
annotations:
summary: "Port {{ $labels.port_num }} on {{ $labels.name }} missing speed label"
description: "Port has active link but name '{{ $labels.port_name }}' doesn't follow naming convention"
# 10G ports degraded
- alert: UnifiLinkSpeedDegraded10G
expr: unifi_port_speed_bps{port_name=~".+-10G"} < 10000000000
for: 2m
labels:
severity: warning
annotations:
summary: "10G port {{ $labels.port_name }} on {{ $labels.name }} degraded"
description: "Expected 10Gbps, negotiated {{ $value | humanize }}bps"
# 2.5G ports degraded
- alert: UnifiLinkSpeedDegraded2_5G
expr: unifi_port_speed_bps{port_name=~".+-2\\.5G"} < 2500000000
for: 2m
labels:
severity: warning
annotations:
summary: "2.5G port {{ $labels.port_name }} on {{ $labels.name }} degraded"
description: "Expected 2.5Gbps, negotiated {{ $value | humanize }}bps"
# 1G ports degraded (explicit -1G suffix)
- alert: UnifiLinkSpeedDegraded1G
expr: unifi_port_speed_bps{port_name=~".+-1G"} < 1000000000
for: 2m
labels:
severity: warning
annotations:
summary: "1G port {{ $labels.port_name }} on {{ $labels.name }} degraded"
description: "Expected 1Gbps, negotiated {{ $value | humanize }}bps"Add to kubernetes/apps/observability/vmrules/kustomization.yaml:
- ./unifi-alerts.yaml<description>-<expected-speed>
| Suffix | Expected Link Speed | Alert Threshold |
|---|---|---|
-10G |
10 Gbps | < 10,000,000,000 bps |
-2.5G |
2.5 Gbps | < 2,500,000,000 bps |
-1G |
1 Gbps | < 1,000,000,000 bps |
-100M |
100 Mbps | No alert (intentionally slow) |
NAS-10G- NAS on 10GbE SFP+Proxmox-2.5G- Server on 2.5GbEOfficePC-1G- Workstation on gigabitSmartPlug-100M- IoT device, intentionally slow
- Unnamed ports with active links trigger
UnifiPortMissingSpeedLabel -100Mports excluded from all degradation alerts- Adding new devices: just name the port in UniFi UI
No changes required. The unpoller Helm chart creates a PodMonitor which the VictoriaMetrics operator auto-converts to VMPodScrape. The observability namespace is already in the allowed list for podScrapeNamespaceSelector.
- Check unpoller pod is running:
kubectl get pods -n observability -l app.kubernetes.io/name=unpoller - Verify metrics are being scraped:
curl -s http://unpoller.observability:9130/metrics | grep unifi_port_speed_bps - Query VictoriaMetrics:
unifi_port_speed_bpsshould return data for all switch ports - Check alerts in vmalert UI:
https://vmalert.${SECRET_DOMAIN}
| Action | File |
|---|---|
| Create | kubernetes/apps/observability/unpoller/ks.yaml |
| Create | kubernetes/apps/observability/unpoller/kustomization.yaml |
| Create | kubernetes/apps/observability/unpoller/helmrelease.yaml |
| Create | kubernetes/apps/observability/unpoller/externalsecret.yaml |
| Create | kubernetes/apps/observability/vmrules/unifi-alerts.yaml |
| Edit | kubernetes/apps/observability/kustomization.yaml (add unpoller) |
| Edit | kubernetes/apps/observability/vmrules/kustomization.yaml (add unifi-alerts) |