Skip to content

Policies

A RightsizingPolicy says which workloads to rightsize and how: the mode of CPU and memory, the apply strategy, the guardrails, HPA and JVM handling, and the profile that shapes the recommendation. It is cluster-scoped, and its short name is rsp.

An example

apiVersion: kerf.getkerf.dev/v1alpha1
kind: RightsizingPolicy
metadata:
  name: team-a-cpu-auto
spec:
  priority: 100
  selector:
    namespaces: ["team-a-*"]
    excludeNamespaces: ["team-a-sandbox"]
    matchLabels:
      tier: backend
    kinds: [Deployment, StatefulSet]
  profile: production
  cpu:
    mode: auto-patch
    limits: keep
  memory:
    mode: audit
    limitEqualsRequest: true
  apply:
    strategy: in-place
  guardrails:
    maxChangePercent: 30
    minConfidence: "0.8"

Every field is optional. A field you leave out inherits its value from the layer below (see Layers). Durations such as cooldown are strings like 24h. The full field list is in the custom resources reference.

Selectors

A policy selects workloads with spec.selector. All criteria you set must match. An empty selector matches every workload.

Field Matches
namespaces A workload whose namespace matches any glob, such as team-*
excludeNamespaces Never matches a namespace that fits any glob, even if namespaces lists it
matchLabels Labels that must all be present with equal values on the workload object, not the pod template
kinds Deployment, StatefulSet, DaemonSet or CronJob
workloads An exact kind, namespace and name. The hub uses it for the override of one workload

How a policy is chosen

Several policies can match one workload. Only one wins:

  1. The highest spec.priority wins (0 to 1000000).
  2. On a tie, the policy name that sorts first wins.

Fields are not merged across competing policies: the winner supplies its fields, and everything it leaves unset comes from the layers below. A policy that loses is still listed in the workload's effective policy, with the reason. status.matchedWorkloads counts the workloads for which a policy is the winner.

Layers

The effective policy of a workload is built from layers. Each field is resolved separately, from the highest layer that sets it:

Layer, lowest first Where it comes from
Built-in defaults Mode audit, strategy in-place, profile production, and the guardrail defaults
Global default The hub policy ro-global-defaults, sent to every cluster
Cluster default The agent chart value clusterDefaults, then the hub policy ro-cluster-defaults
Policy The winning policy: a hub policy or a RightsizingPolicy from Git
Workload override The hub policy ro-override-…, created from the workload page in the UI
Annotation kerf.getkerf.dev/… annotations on the workload (modes)

Hub defaults and overrides are layers, not competitors: they never win or lose on priority. The hub gives its policies these priorities: global default 0, cluster default 10, your policies 1 to 899999, a workload override 900000. Names starting with ro- are reserved for the hub.

A list of maintenanceWindows or freezeWindows set in a higher layer replaces the list from a lower layer. The cluster-wide freeze set on the agent applies in addition to every policy.

Hub policies and Git policies

Policies you create in the UI live in the hub. Each agent writes them to its cluster as RightsizingPolicy resources labelled kerf.getkerf.dev/managed-by=hub and changes only resources with that label. Policies you commit to Git keep working, take part in the choice by priority, and show read-only in the UI. Each cluster has a policy source: hub, git or both. See GitOps.

The effective policy

The workload page in the UI shows the effective policy: every layer, whether it applied, and which layer set each field. The same data is at GET /v1/workloads/{id}/effective-policy (hub API). On the cluster, kubectl get rsr -A shows the effective modes and strategy of each workload.

Profiles

The profile sets how the hub turns usage into a recommendation. CPU is a percentile of usage plus headroom. Memory is the higher of a percentile and the peak, plus a buffer.

Profile Usage window CPU percentile Memory percentile CPU headroom Memory buffer
dev 7 days 0.90 0.95 10 % 10 %
production (default) 14 days 0.95 0.99 15 % 15 %
high-availability 28 days 0.99 0.99 30 % 25 %

These are the built-in values. An admin can change them under Admin → Recommender. A container that was recently throttled or OOM-killed never gets a decrease for that resource.

Three more fields change the recommended values: cpu.limits (keep by default, remove, or ratio with cpu.limitRatio) and memory.limitEqualsRequest (true by default). When a policy changes a profile or one of these fields, the agent ignores recommendations computed with the old settings (RecommendationStale) until the next hub run.