3.4 KiB
CPU sizing and balancing
Review on 2026-10-31
Prometheus was queried for the 95th percentile of five-minute CPU rates over its available history within the previous 14 days, sampled every 15 minutes:
quantile_over_time(0.95,
(sum by(namespace,pod,container)
(rate(container_cpu_usage_seconds_total{container!="",container!="POD"}[5m]))
)[14d:15m])
The largest observed replica value was considered for shared workload sizing. New pods have shorter histories; this is not a guarantee of fourteen complete days per workload. These measurements include the recent recovery period.
The starting recommendation is p95 times 1.2, rounded upwards. Request floors are retained for startup, databases, recovery, and bursty workloads. Requests are not CPU limits. Memory requests and limits are unchanged.
Reviewed changes include Elasticsearch/Kibana, Netflow, Authentik, Traefik, InfluxDB, MKTXP, Mastodon, Paperless, scanner/PDF editor, Uptime Kuma, TURN, Home Assistant, WordPress, Grafana and Prometheus. Busy services without CPU requests receive explicit reservations. Elasticsearch retains a 500m floor per data node for startup/recovery, substantially above its measured baseline.
Matrix is still a legacy Helm release. Its canonical values live in
matrix/authentication/values.yaml in the homelab workspace: the main Synapse
process gets 500m, federation sender 225m, and other workers 50m. Init-container
requests can make effective pod reservations higher than the main container.
The shared RBD claim and worker/offload affinity remain unchanged.
Ceph daemons, the API server, Cilium, CoreDNS, Argo CD, and ingress already have
no CPU limits. Their low or absent requests are not interpreted as spare
physical capacity; Descheduler uses measured CPU. The legacy Ceph CSI operator
CPU cap is removed in rook/deploy/examples/csi-operator.yaml. Ceph daemon
resources are left alone to avoid a storage-wide restart. CPU caps are also
removed from the Elastic operator/data nodes, InfluxDB, MKTXP, and TURN.
Deliberate exclusions
- Do not infer batch, transcoding, game-server or GPU requirements from idle CPU measurements. Their explicit reservations are retained.
- Do not resize workloads that are down or lack representative history.
- Do not automatically rewrite operator-managed database resources or apply VPA mutation alongside Argo. This review changes owning configuration only.
- Loops and Pixelfed charts hard-code some resource requests; overriding an unsupported values key would do nothing. Those require a separate chart interface change before their requests can be tuned through GitOps.
- Small existing requests are retained where a reduction offers little useful headroom. The aim is schedulability and contention protection, not identical requests across applications.
Ongoing operation
Descheduler runs automatically; request changes are reviewed configuration, not a continuously mutating autoscaler. Repeat the history review after a representative week or a substantial workload change. Never set every request from a single current-usage sample. Monitor pending pods, CPU throttling, latency, and node CPU usage as well as reserved capacity.
For a new request change, render the owning chart, inspect all container/init resources, and reconcile through Argo (or the existing legacy Helm installer). Keep the prior commit as rollback; do not delete PVCs to resolve placement.