Week 23: Cost Optimization & FinOps

Every resource this course has provisioned — EC2 instances, Kubernetes nodes, ECR storage, Lambda invocations — has a bill attached, and nothing has asked you to look at it yet. This week does: reading a real cost breakdown, tagging resources so a bill can actually be attributed to a team or project, and the concrete levers — right-sizing, autoscaling, Spot instances, and committed-use discounts — that turn "the AWS bill is high" into a specific, actionable engineering problem. This is also the last module before the capstone.

Module 20 of 22 Week 23 of 26 ~3–4 Hours Hands-on Exercise Included

By the end of this week, you'll be able to

  • Read a Cost Explorer breakdown and tag resources for cost allocation
  • Right-size a workload and run appropriate parts of it on Spot instances
  • Explain what reserved capacity and Savings Plans trade off against flexibility

1. Cost Explorer & Tagging

AWS's Cost Explorer is the starting point for any cost question, and it's only as useful as the tags applied to your resources — group by "Service" and you learn EC2 costs $4,200 last month; group by a team or project tag and you learn which team's EC2 usage that actually is. Every resource created in this course since Week 8 should carry consistent tags for exactly this reason.

main.tf — enforcing tags at the provider level
provider "aws" {
  region = "us-east-1"
  default_tags {
    tags = {
      team        = "platform"
      environment = var.environment
      managed_by  = "terraform"
      cost_center = "eng-infra"
    }
  }
}

default_tags at the provider level, from Week 9's Terraform, applies these tags to every resource the provider creates without repeating them in every resource block — which matters because a tagging policy that depends on every engineer remembering to add tags manually reliably ends up with gaps. Once tags are consistent, AWS Cost Anomaly Detection can alert automatically when a specific tagged group's spend jumps unexpectedly, the cost equivalent of Week 21's burn-rate alerting.

Activate a cost allocation tag before you need it, not after

A tag applied to new resources going forward doesn't retroactively appear on Cost Explorer reports for spend that already happened — and AWS cost allocation tags specifically need to be activated in Billing settings before they show up in reports at all, a step separate from just applying the tag. Set both up in Week 8, and this week's exercise reflects months of real, attributable data instead of one week's worth.

2. Right-Sizing, Autoscaling & Spot

Right-sizing means matching an instance's actual CPU/memory utilization to what it's provisioned for — AWS Compute Optimizer analyzes real CloudWatch usage and recommends a smaller (or larger) instance type based on observed load, not on the guess made when the resource was first provisioned. A t3.large running at 8% average CPU utilization is very likely over-provisioned; downsizing it to a t3.small can cut its cost by more than half with no functional change.

Autoscaling — the Kubernetes Horizontal Pod Autoscaler from Week 12, or an EC2 Auto Scaling Group — solves a related but distinct problem: instead of provisioning for peak load permanently, capacity tracks actual demand, scaling down (and reducing cost) automatically during low-traffic periods rather than running peak-sized infrastructure around the clock.

terraform — a mixed On-Demand + Spot node group
resource "aws_eks_node_group" "spot" {
  cluster_name    = aws_eks_cluster.main.name
  node_group_name = "spot-workers"
  capacity_type   = "SPOT"        # up to ~70-90% cheaper than On-Demand
  instance_types  = ["t3.large", "t3a.large", "t3.xlarge"]  # multiple types = better availability

  scaling_config {
    desired_size = 3
    min_size     = 1
    max_size     = 10
  }
}

Spot instances use AWS's spare capacity at a steep discount, with one real trade-off: AWS can reclaim a Spot instance with a two-minute warning if it needs the capacity back. That makes Spot the right fit for stateless, horizontally-scaled workloads that tolerate a Pod being interrupted and rescheduled — exactly what Week 12's readiness probes and multi-replica Deployments already handle gracefully — and the wrong fit for a StatefulSet's database Pod from Week 15, where losing the node mid-write is a much bigger problem.

Diversify instance types on a Spot node group, not just count

Requesting only one instance type means every Spot instance you hold competes for the exact same limited capacity pool, increasing the odds of a mass reclaim event all at once. Listing several similar instance types, as in the example above, spreads the request across separate capacity pools — Kubernetes' scheduler doesn't care which type a Pod lands on, so there's no real downside to offering the scheduler more options.

3. Commitments & the FinOps Team Model

For a workload that genuinely runs continuously — unlike the interruption-tolerant case Spot is built for — Reserved Instances and Savings Plans trade commitment for discount: committing to a 1- or 3-year term (with no-upfront, partial-upfront, or all-upfront payment options) in exchange for up to ~40-60% off On-Demand pricing. A Compute Savings Plan is the more flexible of the two — it applies automatically across EC2, Fargate and Lambda regardless of instance family or region, unlike a Reserved Instance tied to a specific instance type.

FinOps is the organizational practice that makes all of this sustained rather than a one-time cleanup exercise — typically a small cross-functional team (finance, engineering, and often a dedicated FinOps practitioner) that owns cost visibility, sets showback/chargeback reporting per team using the tags from Section 1, and reviews commitment coverage on a recurring cadence rather than only when a bill spikes unexpectedly.

a simple monthly FinOps review checklist
1. Coverage: what % of steady-state compute is covered by
   Savings Plans / Reserved Instances vs. On-Demand?
2. Utilization: are we paying for committed capacity we're
   not actually using?
3. Anomalies: did any tagged team/project's spend jump
   >20% month-over-month, and why?
4. Right-sizing: what does Compute Optimizer recommend this
   month that we haven't acted on yet?
5. Waste: any untagged, orphaned resources (unattached EBS
   volumes, idle load balancers, old snapshots)?
FinOps is an engineering practice, not just a finance one

The single biggest lever in most real cost reviews turns out to be engineering decisions — an over-provisioned instance type, a forgotten dev environment left running, a Job with no activeDeadlineSeconds that hung for days — not a procurement negotiation. Treating cost as a metric engineers watch alongside latency and error rate, not a spreadsheet finance reviews quarterly, is what actually keeps a bill under control.

4. Hands-on Exercise

Hands-on

Tag, analyze, and right-size infrastructure from earlier in the course

Apply a real cost review against your own AWS account.

Requirements:

  1. Add default_tags to every Terraform provider block from earlier weeks and re-apply, then activate the corresponding cost allocation tags in AWS Billing settings.
  2. In Cost Explorer, group your account's spend by service, then by your new tags, and note which single resource or service is the largest contributor.
  3. Check AWS Compute Optimizer's recommendations for any EC2 instance you've been running, and either right-size it or write down why the current size is actually justified.
  4. Convert one EKS node group to use Spot capacity with at least three instance types listed, and confirm existing Pods reschedule successfully after simulating an interruption (kubectl drain a Spot node).
  5. Write a one-page FinOps review using the checklist from Section 3, based on your own account's real numbers.
Hint

Set an AWS Budget alert at a low dollar threshold ($10-20) before doing any of this — it's a five-minute safety net that catches an accidentally-left-running resource well before the next billing cycle surprises you.

5. Knowledge Check

Three quick questions. Expand each to check your answer.

Q1

Why does a consistent tagging strategy need to exist before a cost anomaly happens, not be added afterward?

Cost Explorer and cost allocation tags only report against spend that occurred after the tag was applied and activated — a tag added today doesn't retroactively let you attribute last month's spend by team or project. Setting up tagging from the start (via default_tags in Terraform) means a cost investigation later has real historical data to work from instead of starting the clock at the moment someone finally asked.

Q2

Why are Spot instances a good fit for a Kubernetes Deployment's Pods but a poor fit for a StatefulSet's database Pod?

Spot capacity can be reclaimed with only a two-minute warning, which is tolerable for a stateless Deployment's Pod — it just gets rescheduled elsewhere, and readiness probes ensure traffic doesn't route to it until it's healthy again. A StatefulSet's database Pod being interrupted mid-write, or losing its node unexpectedly, is a much higher-stakes disruption that On-Demand's stability is worth paying for.

Q3

Why is a Compute Savings Plan generally more flexible than a Reserved Instance covering the same spend?

A Reserved Instance discount is tied to a specific instance family and region. A Compute Savings Plan is a commitment to a dollar-per-hour spend level that applies automatically across EC2, Fargate and Lambda regardless of instance type or region, so a team can change instance types or shift workloads to Fargate later without losing the discount they already committed to.