- Every namespace and workload mapped to an owning team and product
- Cost per team and per product reported in the same format finance uses
- Right-sizing applied continuously instead of in occasional clean-up projects
- New services start with sensible requests, limits and autoscaling by default
Anonymised composite. Client names, identifying details and confidential figures are withheld; public facts are cited in the references.
The situation
A fast-growing technology company ran most of its products on a handful of shared Kubernetes clusters. Teams shipped quickly, which was the point. But cloud spend had become one of the largest lines in the budget, and the monthly review was a guessing game: the bill showed compute, storage and network by account, while the organisation thought in teams and products. Requests and limits had been copied between services for years. Some workloads ran around the clock for jobs that happened once a day.
A previous clean-up project had cut costs for a quarter, then spend crept back. The leadership question was simple: how do we make this stay fixed?
What had to be true
- Every workload has an owner: a team and a product, enforced, not hoped for.
- Cost is visible where decisions are made: per team, per product, in a format finance accepts.
- Efficiency is the default: new services start right-sized and autoscaled without teams becoming Kubernetes experts.
- Shrinking is cheap and safe, so teams actually do it.
- No slowdown in delivery; controls live in the paved road, not in tickets.
Architecture
The data flow, end to end
- DeployService created from a template with owner labels and default autoscaling
- AdmitPolicies reject workloads without labels; quotas cap each namespace
- AllocateOpenCost apportions node, storage and shared costs to each workload
- JoinAllocations joined with cloud billing in a common FOCUS format
- ReportCost per team and product, with recommendations, sent to owners
- ActRight-sizing and scaling changes applied through the same templates
OpenCost provides workload-level cost allocation for Kubernetes, attributing cluster costs to namespaces, labels and workloads1. Joining those allocations with billing data in the FinOps Foundation's FOCUS format gave finance and engineering one view of the same numbers2; the reasoning is in FOCUS and AI spend.
The hard part: making shrinking safe
Visibility alone rarely changes behaviour. Teams knew some services were over-provisioned; they left them because changing requests meant restarts, and restarts meant risk. Two Kubernetes changes removed most of that friction:
- In-place Pod resize, stable since Kubernetes 1.35, lets CPU and memory change on running Pods, and the Vertical Pod Autoscaler's in-place mode uses it3. Recommendations could finally be applied continuously, starting with CPU on stateless services. Details in in-place Pod resize.
- Scale to zero with the HorizontalPodAutoscaler, available from Kubernetes 1.37 for workloads driven by suitable object or external metrics such as queue length4. Workers that waited idle most of the day stopped costing anything while idle. See Kubernetes FinOps and scale to zero.
Namespace quotas then capped the total each team could request, which turned capacity into a visible, negotiable budget rather than an unlimited pool5.
Cost programmes fail when saving money is risky and spending it is free. Flip both: make shrinking safe and automatic, and make every request visible to its owner.
How it ran
- Templates and policies
- Allocation and reporting
- Autoscaling defaults
- Adopt templates
- Review weekly cost report
- Accept right-sizing
- Agree allocation rules
- Set team budgets
- Track unit costs
- Ownership labels
- Allocation running
- First team reports
- Service templates
- Admission policies
- Quotas per namespace
- VPA in-place for CPU
- Queue-driven scale to zero
- Node bin packing
- Unit cost metrics
- Budget alerts
- Quarterly review
Key decisions and trade-offs
| Decision | Alternative | Why |
|---|---|---|
| Enforce ownership labels at admission | Tag clean-up campaigns | Unowned workloads can't appear in the first place |
| Open-source allocation plus FOCUS | A separate cost tool per cloud | One allocation model and one billing schema across providers |
| Right-size in place, CPU first | Rolling restarts for every change | Lower risk, so recommendations actually get applied |
| Scale to zero only for queue-backed work | Scale everything to zero | User-facing services need warm capacity |
| Quotas as team budgets | Unlimited shared pool | Makes capacity a visible, negotiated decision |
What I'd do again, and differently
Again: start with ownership and allocation, and put every improvement into the templates so it spreads automatically. Differently: agree unit cost metrics with product leaders earlier. Cost per team tells you who spends; cost per customer action tells you whether the spend is worth it.
If cloud costs are outgrowing the business, my fractional CTO work covers platform and FinOps programmes like this, or analyse your challenge and bring the result to a call.
- 1OpenCost FAQOpenCost project
- 2FOCUS specificationFinOps Foundation
- 3Kubernetes 1.35: In-Place Pod Resize Graduates to StableKubernetes blog · 19 Dec 2025
- 4Kubernetes v1.37: Scale Workloads to Zero with HorizontalPodAutoscalerKubernetes blog · 2 Sep 2026
- 5Resource QuotasKubernetes documentation