Optimisation & nodes
Right-Sizing
Right-Sizing measures what your workloads actually consume and recommends Kubernetes resources.requests and resources.limits values that fit the observed load - so over-provisioned workloads don’t waste capacity, and under-provisioned ones don’t get OOM-killed or CPU-throttled. It works on Deployments, DaemonSets, StatefulSets, and CronJobs (the last uses a different, per-run flow - see the CronJobs section below).
When to use it
- A workload is missing
resources.requests/limitsentirely and you want sensible starting values - An existing workload over- or under-allocates and you’d like a defensible adjustment
- You want a quick health-check on resource sizing before a load test, traffic increase, or quota review
When an analysis finishes, Kubius sends a Right-sizing complete notification (toggle it in Settings → Notifications → Workloads).
Where it lives
Open the Right-Sizing entry in the sidebar (under the Features section, at the top). It lists every Deployment, DaemonSet, StatefulSet, and CronJob in the current namespace scope, with name filter and column sort. System namespaces are always excluded - restarting an operator-managed workload for right-sizing would be high-risk.
Columns:
- Name - workload name and icon
- Kind -
Deployment,DaemonSet,StatefulSet, orCronJob - Right-Sizing - current status pill (
Not analyzed,Sampling 47%,Last: 5m ago,Failed, …) - Namespace
- CPU and Mem - current
requests / limitsfrom the workload spec, or just the request if no limit, or-if neither is set
Click a row to open the analysis page in the right panel.
Filtering
The filter bar has a text filter (matches workload name) plus two dropdowns:
- Kind - All kinds / Deployment / DaemonSet / StatefulSet / CronJob
- Namespace - All namespaces, or any single non-system namespace that has an analyzable workload
All three combine. A Clear all history button also appears here when saved history exists - see the History section below.
Jumping to the underlying workload
The analysis page header shows the workload name (with a copy button next to it) and a View Deployment / DaemonSet / StatefulSet / CronJob pill on the right. Click the pill to switch to that workload’s sidebar entry with it open in the detail panel - useful for inspecting events, conditions, or the full YAML mid-analysis.
Back / forward navigation
Right-Sizing selections are recorded in browser-style navigation history alongside every other detail view. ⌘[ / ⌘← goes back, ⌘] / ⌘→ goes forward - so you can drill from a Pod into its Deployment into Right-Sizing for that Deployment and walk back through each step.
Sidebar badges
While you work elsewhere in the app, two coloured pills appear next to Right-Sizing in the sidebar:
- Blue waveform - number of right-sizing analyses currently running
- Green seal - completed analyses you haven’t opened yet
The green count decrements when you open a completed result; the blue count tracks live work.
Running an analysis
The analysis page opens on the ANALYZE section, which contains:
- How recommendations are calculated - a short explainer of the formulas (see below).
- Multipliers - a 2×2 grid (CPU / Memory × Request / Limit) of the boost factors applied to observed usage. Defaults are 1.2 / 1.5 for CPU request / limit and 1.2 / 2.0 for memory request / limit. Edit any cell to tune the recommendation; Reset restores the defaults. Values are clamped to
[0.1, 10]and each limit is kept ≥ its request. Multipliers are remembered per workload, and because recommendations are computed at read time, changing them re-evaluates past results too. - Sample duration -
1 / 5 / 15 / 30 / 60 minutes, then2 / 4 / 8 / 12 / 24 hours. 15 minutes is the recommended minimum and the default. Longer windows capture periodic bursts and diurnal patterns; shorter ones are for quick spot-checks. - Restart … first - keep this on if you want the analysis to include startup CPU/memory spikes (strongly recommended).
- Start analysis - kicks off the sampling.
Current requests / limits are shown inline on each container card in the Recommendations section, so there’s no separate “Current” block. Your last-used duration and restart-first choice are remembered across sessions.
System namespaces are excluded. Right-Sizing never lists workloads from
kube-system,gke-system,config-management-system, or any other system namespace. Restarting an operator-managed workload to right-size it is high-risk and almost never the intent.
What happens during sampling
- Restart (optional) - Kubius patches
kubectl.kubernetes.io/restartedAton the workload, triggering a rolling restart - Wait for fresh pods - pre-restart pod names are snapshotted; we wait until at least one pod outside that set reports metrics
- Sample - every 3 seconds for the duration, per-container CPU and memory readings are collected from
metrics.k8s.ioacross every pod owned by the workload - Aggregate - per-pod stats are computed, then the highest figure across replicas is used for the recommendation (see below)
Note on long durations. Sampling polls every 3 seconds regardless of the chosen window, so multi-hour runs collect a large number of samples (and metrics-server only scrapes every ~15s, so several consecutive reads are identical). The multi-hour options are best for capturing periodic or diurnal load; for most workloads 15–60 minutes after a restart is plenty.
A floating tray appears in the bottom-right while analyses are running. Click any row to jump straight to its analysis page - Kubius switches to the Right-Sizing section and opens that workload’s analysis. The header has a chevron to collapse and an X to hide the tray entirely (the analyses keep running). The row’s status line shows time remaining ticking down every second.
Live timer on the detail page
While sampling, the Progress section shows a progress bar plus a live readout: 5m 23s of 15m · 9m 37s remaining (hours are shown as e.g. 2h 14m for the longer windows). Both elapsed and remaining update every second so you always know exactly how long is left, independent of the 3-second sample cadence.
How recommendations are calculated
For every container, observed usage is multiplied by the configurable multipliers (defaults shown):
- CPU request = p95 ×
CPU requestmultiplier (default 1.2) - CPU limit = max ×
CPU limitmultiplier (default 1.5), clamped to be ≥ the recommended request - Memory request = p95 ×
Mem requestmultiplier (default 1.2) - anchored to steady-state usage so the scheduler bin-packs against typical consumption, not transient peaks - Memory limit = max ×
Mem limitmultiplier (default 2.0), clamped to be ≥ the recommended request - kept well above peak so warmup/startup spikes have headroom
Memory request and limit are deliberately decoupled: the request targets steady state (p95) for scheduling, while the limit is anchored to observed peak with generous headroom. CPU never OOM-kills (it’s only throttled), so CPU stays request = p95, limit = max. Adjust any of the four multipliers in the ANALYZE → Multipliers grid; changes re-evaluate existing results immediately.
Rounding
Each recommended figure is then rounded up to a tidy, size-aware step - so the numbers read cleanly, the YAML is easy to maintain, and re-running an analysis doesn’t churn the values by a megabyte here or a millicore there. The step grows with the value so it never inflates a recommendation noticeably:
- CPU - to the nearest 10m below 100m, 25m below 250m, 50m below 1 core, then 100m.
- Memory - to the nearest 8Mi below 256Mi, 32Mi below 1Gi, 128Mi below 4Gi, 256Mi below 16Gi, then 512Mi.
Rounding is always upwards, never to the nearest, so a recommendation can never land below what was actually observed - important for memory, where under-provisioning means an OOM-kill. Rounding happens once, after the multipliers are applied, and the exact observed peak is still shown alongside the recommendation so nothing is hidden. The figure you see in the cards, in the previewed YAML, and in what Apply writes to the cluster are all identical.
Multi-replica handling
Every replica gets the same PodSpec but their loads can diverge (sticky sessions, sharded work, uneven load balancing). Merging all replicas into one bag of samples would let a cold pod drag the p95 down and under-provision the hot one. Kubius instead computes per-pod stats and uses the highest across replicas as the input to the recommendation. The header for each container card shows max across N replicas whenever more than one pod was sampled.
Reading the results
The Recommendations section renders a per-container card with:
- A row showing p95 / max / current / recommended for CPU Request and CPU Limit
- The same for Memory Request and Memory Limit
Below that, the Latest recommended YAML block is selectable text with a copy pill - paste it straight into your workload manifest. The Apply button lives inside the caution panel next to it:
# Right-sized for sendhit-api-development
# 92 samples across 1 replica
resources:
requests:
cpu: 10m
memory: 103Mi
limits:
cpu: 20m
memory: 120Mi
For multi-container pods, each container gets its own labelled resources: block.
Applying recommendations
The Apply Recommendations button (inside the caution panel) issues a strategic-merge PATCH to spec.template.spec.containers[*].resources - preserving image, ports, env, and everything else. It targets the right apps/v1 resource for the workload kind (deployments, daemonsets, or statefulsets). This triggers a fresh rollout, which the alerts engine will notify you about when it completes. (For CronJobs the patch path is spec.jobTemplate.spec.template.spec.containers on the batch/v1 cronjob, so it affects future runs - see the CronJobs section.)
Apply works against either a live analysis (just-completed) or a historical result.
History
Every successful analysis is persisted to ~/Library/Application Support/Kubius/right-sizing-history.json. The last 10 results per workload are kept.
- Opening a workload immediately shows the most recent persisted result if there’s no active session - no need to re-run to see prior recommendations
- The Previous analyses accordion at the bottom of the page lists earlier runs, latest first. Expand a row to see its per-container cards and YAML snippet. The trash icon removes a single entry.
- The header next to Latest recommended shows the timestamp of the result being displayed
Clearing history
- One resource - the Clear history button in the Latest recommended header (shown only when that resource has saved history) removes all of its persisted analyses after a confirmation.
- All resources - the Clear all history button in the list’s filter bar (shown when any history exists for the current cluster) removes saved history for every resource in the cluster, after a confirmation telling you how many are affected.
Clearing history only deletes the saved analysis records - it never changes a workload’s live requests / limits.
CronJobs
CronJobs are right-sized differently because their pods are ephemeral - a Job spawns a pod that runs to completion and exits, so there’s no steady state to restart-and-sample. (An exit code of 137 = 128 + SIGKILL, almost always an OOMKill - the container exceeded its memory limit. Right-sizing helps you find the limit it actually needs.)
Instead of a duration, the CronJob ANALYZE section samples one run:
- Trigger run now & sample - creates a one-off Job from the cronjob’s
jobTemplate(equivalent tokubectl create job --from=cronjob/…) and samples it immediately. This runs the real workload, so any side effects (data writes, notifications, etc.) happen - use it only when an ad-hoc run is safe. - Watch next scheduled run - creates nothing; waits for the cronjob’s next scheduled Job to appear and samples that. No extra side effects, but keep the page open until the runs you asked for have finished.
- Runs to watch - how many scheduled runs to collect before recommending (1 / 2 / 3 / 5 / 10). Watching several runs covers load that varies between runs; the samples accumulate and the recommendation uses the heaviest run (per-pod max, same as multi-replica handling). The tray and page show “run X of Y” as it progresses. If a later run doesn’t appear within an hour, Kubius finishes with the runs collected so far rather than failing. (Applies to Watch; Trigger run now always samples a single run.)
- Max sampling time - a per-run safety cap; sampling of each run stops as soon as its pod finishes, or when the cap elapses.
While the run’s pod is alive, Kubius polls its per-container CPU/memory from metrics.k8s.io and reads the node’s cAdvisor container_memory_max_usage_bytes (the peak since the container started). The cAdvisor peak is folded into the memory limit recommendation - crucially, this captures the high-water mark even for runs too short for metrics-server (~15s scrape interval) to ever sample. If a run is so short that only cAdvisor sees it, the memory recommendation still works; CPU is shown as not captured in that case (cAdvisor CPU is a counter Kubius doesn’t rate).
Applying patches spec.jobTemplate.spec.template.spec.containers[*].resources, so it affects future runs - any already-running Job is unaffected. cAdvisor access needs nodes/proxy RBAC; without it, sampling falls back to metrics-server only.
Sharing results across your team (optional)
By default, right-sizing results are saved only on your Mac. Kubius Pro can optionally store them in your own cluster so teammates see the same history - off by default, opt-in, encrypted, and never sent to Kubius. See Right-sizing persistence for the full guide.
Tips
- For short-startup workloads, 5 minutes can be enough; for steady-state work with periodic bursts, prefer 30–60 minutes. The multi-hour windows (up to 24 hours) suit workloads with diurnal or scheduled load you want to capture in full. The 1-minute option exists for very quick spot-checks where you only care about the startup spike.
- The restart is what surfaces startup spikes - if a workload has been running for hours and you want only steady-state, switch the toggle off
- If a Deployment or StatefulSet has
replicas: 0no sampling is possible - start it first (DaemonSets schedule per-node and have no replica count) - Tune the Multipliers grid to bias toward headroom (raise the limit multipliers) or density (lower them); changes persist per workload and re-evaluate existing results
- The applied recommendations honour your CPU/Memory unit preference for display but the YAML always uses
m(millicores) andMi(mebibytes) since those are stable across formats