Writing

A FinOps teardown of my own AWS bill

· 3 min read · by Christopher J. Mahan

My day job is FinOps for a large AWS environment. So when my personal AWS bill, which runs eight side products, more than doubled in a month, I ran the same playbook on myself. Most of the jump wasn’t what it looked like, and the one real fix taught me something about “simple” rightsizing.

Step 1: Break the bill down before reacting

Total spend went from about $21 to about $47 month over month. Grouped by service, and then by usage type, October looked like this:

Line itemCostRecurring?
Domain registrations (two new domains)$33.98Yearly
Route 53 hosted zones (11 × $0.50)$5.31Monthly
EC2 t3.micro (new this month)$3.82Monthly
Public IPv4 address$1.85Monthly
CodeBuild minutes$0.83Per deploy
EBS gp3, 16 GB$0.63Monthly

Lesson one: the biggest line wasn’t a cost problem. Two domain purchases were 70% of the month. Registrar charges are lumpy: any month with a purchase or renewal looks like a spike. A forecast built on that month (AWS’s own forecast said $50) is wrong, because it extrapolates one-time charges.

Step 2: Find the real run rate

Strip out the one-time charges and annualize. The product behind the new EC2 line costs about $15 a month at steady state:

  • t3.micro, 730 hours: $7.59
  • Public IPv4: $3.65. Since 2024, AWS charges for every public IPv4 address, and for a tiny instance that’s about a quarter of the cost.
  • 16 GB gp3: $1.28
  • Builds, the hosted zone, and the domain spread over 12 months: about $2.50

Step 3: Fix the alerting before the spend

The product had a $1/month budget alert. Normal operation trips it every month, so it was pure noise, and noise is how real alerts get ignored. I raised it to $20, just above the run rate, so it only fires on something real: a runaway build loop, a bigger instance, a traffic surprise.

Step 4: The one real optimization, t3 to Graviton

A t4g.micro (ARM/Graviton) costs about 20% less than a t3.micro for the same vCPUs and memory. On paper that’s a one-line change. In practice, changing CPU architecture touched five things:

  1. The machine image. The CloudFormation template pinned the x86_64 Amazon Linux AMI. I made it conditional on the instance family.
  2. The Node.js download in the bootstrap script was hard-coded to linux-x64.
  3. The build environment. The app bundles native binaries (sharp for image processing), so CodeBuild must build on ARM too.
  4. The release cache. Releases are content-addressed so unchanged code is never rebuilt. Without the architecture in the key, the deploy would have happily reused the x86 build on the ARM server.
  5. Parameter drift. aws cloudformation deploy keeps a parameter’s previous value unless it’s passed, so changing the template default alone would have done nothing.

I previewed a change set first. It replaced only the instance, and the Elastic IP was modified, not replaced, so the address stayed the same.

Step 5: The bug the migration exposed

The first deploy failed: the new server’s first-boot setup finished 16 seconds after the deploy tried to install the app. The x86 server had always been booted long before any deploy, so nobody had noticed the race. The fix is one line: wait for cloud-init status --wait before installing. That gap had been there all along; replacing the server just made it show.

Results

  • The steady-state cost of that product dropped about 20% on compute.
  • The budget alert now means something.
  • The deploy pipeline is architecture-aware and can’t install a mismatched build.
  • The forecast confusion is explained: registrar charges are planned yearly, not treated as drift.

The takeaway

Don’t start a FinOps review by cutting. Start by classifying each charge as one-time or recurring, and as waste or the cost of running the product. Here the “spike” was planned spending, the noisiest problem was an alert, and the only real optimization was a small rightsizing that needed real engineering to do safely.

At enterprise scale the numbers have more zeros, but the method is the same.

Christopher J. Mahan is a Lead FinOps Engineer and cloud architect who builds and runs his own products. More about me · RSS

Hiring for cloud, FinOps, or data & AI?

I bring enterprise-scale AWS and FinOps experience, plus the habits of someone who ships and runs his own products. Let's talk.