Right-Sizing Cloud Compute: Cut 30 to 50 Percent Off Your Bill Without Downtime
Founder & Principal, HOUSE603 · CISSP, CISM
A big chunk of almost every cloud bill is slack. Instances get picked for a peak that rarely shows up, nobody circles back, and you keep paying for CPU and memory that sit idle. Right-sizing is the unglamorous fix: measure what a workload actually uses over a few real weeks, then move it onto a machine that fits. We reach for it first on nearly every cost review because it carries almost no risk, users never feel it, and it usually frees more money than any clever re-architecture would.
Ask a finance team where the cloud money goes and you usually get a shrug. Compute is nearly always the biggest line on the invoice, and the biggest waste inside it is rarely anything exotic. It is ordinary virtual machines running two or three sizes larger than the work needs. What follows is the sequence we run on a HOUSE603 cost review, in order, with the commands we actually type. None of it is clever. That is the point.
Why over-provisioning is the default
It starts innocently. A service ships, nobody knows how much traffic it will take, so someone picks a comfortable size. Traffic grows. There is a scare one afternoon and an engineer bumps the instance to be safe. The size never comes back down. Do that across thirty services over a couple of years and you get a bill that tracks the team's anxiety rather than its traffic.
The reason it persists is that shrinking an instance feels risky and saving money is nobody's on-call page at 2 a.m. So the numbers only ever go up. Breaking that pattern does not take heroics. It takes two or three weeks of honest measurement and the nerve to act on what the graphs say instead of what the last incident felt like.
Step 1: Baseline with real metrics
Collect two to four weeks so you catch the weekly rhythm, month-end batch jobs and all. Average CPU will lie to you here, so pull the 95th percentile alongside it. A box that averages six percent but hits seventy every Monday morning is not the same box that sits flat at six all week, and only the p95 tells them apart. On AWS the CLI gives you both in one call:
# p95 CPU for one instance over 14 days (3600s periods)
aws cloudwatch get-metric-statistics \
--namespace AWS/EC2 --metric-name CPUUtilization \
--dimensions Name=InstanceId,Value=i-0abc123def456 \
--start-time "$(date -u -d '14 days ago' +%FT%TZ)" \
--end-time "$(date -u +%FT%TZ)" \
--period 3600 --statistics Average --extended-statistics p95 \
--query 'Datapoints[].{t:Timestamp,avg:Average,p95:ExtendedStatistics.p95}' \
--output table
Azure gives you the same shape of data with az monitor metrics list, and GCP with the Monitoring API or gcloud monitoring. Wherever you look, the tell is identical. If p95 CPU sits under roughly ten percent for weeks with plenty of room above it, that instance is a candidate to shrink. Note the word candidate. You are not done, because CPU is only one dimension.
Step 2: Memory and I/O matter as much as CPU
This is where a quick win turns into an outage if you rush. A machine can idle on CPU while pinning memory or maxing out disk throughput, and CPU-only right-sizing walks straight into that. AWS makes the trap easy to fall into because the default EC2 metrics do not report memory at all; you have to install the CloudWatch agent to see it. Before you touch anything, confirm memory, network, and EBS throughput on every candidate. A memory-bound service quietly moved onto a CPU-optimized family is exactly how a tidy Friday saving becomes a Sunday-night page.
Step 3: Pick the right family, then the right size
- Spiky, mostly idle workloads suit burstable families (AWS T-series, Azure B-series) that bank credits during quiet periods.
- Steady, CPU-heavy services want compute-optimized families (C-series).
- Memory-heavy caches and databases want memory-optimized families (R-series).
Get the family right first, then step the size down one notch at a time and watch a full day before the next drop. Resist making one big move to feel efficient; small steps are what keep this boring. And where the workload allows it, prefer two smaller instances behind a load balancer over a single large one. It usually costs a little less, and it turns losing a node into a shrug rather than an outage.
Step 4: Prove it in staging, then apply with code
Do not resize by clicking around the console. A console change is invisible to the rest of the team, unreviewed, and awkward to undo. Put the size in infrastructure-as-code instead, so it gets a pull request, a second pair of eyes, and a one-line road back if the graphs turn ugly:
# variables.tf
variable "web_instance_type" {
type = string
default = "t3.small" # was m5.large
description = "Right-sized after 4 weeks of p95 < 8%"
}
# main.tf
resource "aws_instance" "web" {
ami = var.ami_id
instance_type = var.web_instance_type
tags = { Name = "web", RightSized = "2026-09" }
}
Push it through staging with a load test that looks like real traffic, watch the same metrics for a day, then promote during working hours when someone is actually looking at the dashboards. Because the size is a variable, backing out is one line and a re-apply, not an emergency.
Step 5: Automate the savings so they stay
Here is the part almost everyone skips. Right-sizing decays. Six months on, someone has quietly bumped three instances back up after a busy week and you are paying for slack again. Two habits keep it honest. Autoscale the tier that genuinely varies, so capacity follows demand instead of a human's nerves. And switch non-production off out of hours. Dev and staging that sleep at night and on weekends run for roughly a third of the hours of an always-on setup, which is close to a two-thirds cut on those environments for one line of cron:
# stop dev fleet weeknights at 20:00 (cron on a control host)
0 20 * * 1-5 aws ec2 stop-instances --instance-ids \
$(aws ec2 describe-instances \
--filters "Name=tag:Env,Values=dev" "Name=instance-state-name,Values=running" \
--query "Reservations[].Instances[].InstanceId" --output text)
Step 6: Lock in a discount on what is left
Discounts come last, and this is the mistake we see most often. A team buys three years of Reserved Instances against their current footprint, feels clever, and locks in every oversized box for the length of the contract. Right-size first. Once the running footprint is honest, cover the steady baseline with Savings Plans or Reserved Instances on AWS, Reservations on Azure, or Committed Use Discounts on GCP, and leave the spiky top of the curve on-demand or on spot. Buy commitment against reality, never against waste.
A realistic result
On the mixed fleets we assess, the monthly bill usually comes down somewhere between a third and a bit under a half. Roughly half of that is resizing and switching off idle non-production, and the rest is a sensible commitment on the baseline. None of it is anything a user can feel. Your numbers will not match ours, and that is the point: measure your own before and after, on your own account, and trust that over any headline percentage, including this one.
Want the exact number for your account, and a plan to hit it?
HOUSE603 runs a fixed-scope cloud cost review that turns these steps into a right-sizing plan and the infrastructure-as-code to apply it, without downtime.
Book a cloud cost review →