Command Palette

Search for a command to run...

Hectal

Guide G3 · DevOps path

Cloud Cost Optimization (FinOps)

Where cloud money actually goes, right-sizing, Savings Plans and Reserved Instances, Spot, storage lifecycle, the hidden costs of NAT gateways and data transfer, Kubernetes cost allocation, and budgets and anomaly alerts.

Intermediate 50 min

Start here

The mental model

The cloud is a taxi meter that never stops: every server, disk, snapshot, and gigabyte crossing a boundary is billed by the second or the byte, whether anyone uses it or not. Cost optimization is making sure the meter only runs for things that deliver value.

FINOPS is the practice of making engineers see and own the cost of what they build, just as DevOps made them own reliability. The three phases: INFORM (who spends what: tags, allocation, dashboards), OPTIMIZE (right-size, commit, clean up), OPERATE (budgets, alerts, reviews, cost as a design input).

Go deeper

How it works inside

01Visibility first: tags and allocation

You can't optimise what you can't attribute. Tag every resource with team, service, env (Terraform default_tags, Crossplane compositions, Karpenter node tags) and activate them as COST ALLOCATION TAGS in billing. COST EXPLORER groups spend by tag, service, account, and usage type; the CUR 2.0 / Data Exports feed goes to Athena for detailed queries. In Kubernetes, one EC2 bill covers many teams, so OPENCOST or KUBECOST splits node cost by pod requests and usage (Platform course, Mission 2.4).

02Compute: right-size, commit, Spot

RIGHT-SIZE: most instances and pod requests are over-provisioned. Use Compute Optimizer and Kubernetes VPA recommendations, and lower requests to what p95 usage justifies (Kubernetes course, requests and limits); Karpenter then packs nodes tighter. Graviton (arm64) instances are typically ~20% cheaper for similar performance if your images are multi-arch.

COMMIT for the steady baseline: COMPUTE SAVINGS PLANS (a $/hour commitment for 1 or 3 years, applies across EC2 families, regions, Fargate, and Lambda, up to ~66% off) are the flexible default; EC2 Instance Savings Plans and RESERVED INSTANCES give more discount for less flexibility; RDS, ElastiCache, and OpenSearch have their own reservations. Buy for the baseline you're sure of, not the peak.

SPOT for interruption-tolerant work (stateless services with replicas, CI runners, batch): up to ~90% off (Platform course, Karpenter). Also: switch off non-production at night and weekends (a 12×5 schedule cuts that spend ~65%).

03Storage

S3 LIFECYCLE RULES move objects to cheaper classes (Standard-IA, Glacier Instant/Flexible/Deep Archive) and expire them; INTELLIGENT-TIERING does it automatically for unknown access patterns. Clean up incomplete multipart uploads and old versions. EBS: migrate gp2 to gp3 (about 20% cheaper with better baseline performance), delete unattached volumes and old snapshots (Platform course, preview cleanup), and set log retention in CloudWatch Logs (the default is 'never expire').

04Network: the hidden bill

NAT GATEWAY charges per hour AND per GB processed. Pulling container images from ECR or reading S3 through NAT can cost more than the servers. GATEWAY VPC ENDPOINTS for S3 and DynamoDB are free, and interface endpoints for ECR, STS, and CloudWatch cut NAT data (AWS course, NACLs and endpoints). DATA TRANSFER: traffic between AZs is charged in both directions (chatty microservices, Kafka replication, cross-AZ database reads), and egress to the internet is the most expensive. CloudFront reduces origin egress. Topology-aware routing in Kubernetes and Kafka rack-aware fetching (Stateful course, Unit 2.3) keep traffic in-zone.

Network: the hidden billdiagram
Rendering diagram…

05Operate: budgets and anomalies

AWS BUDGETS alert (email/SNS/Slack) on actual or forecast spend per account, team tag, or service. COST ANOMALY DETECTION learns normal patterns and flags spikes, like a forgotten GPU instance or a runaway log volume. Review top movers weekly in a short FinOps meeting with engineering owners, and make cost a design input ('this architecture costs ~$X/month at expected load') in reviews.

Do it

Hands-on lab

  1. 1

    Find the top spend by service and tag

    Cost Explorer from the CLI: last month's cost grouped by team tag. Untagged spend shows as team$ with an empty value, which is your first cleanup target.

    terminal
    $ aws ce get-cost-and-usage --time-period Start=2026-08-01,End=2026-09-01 --granularity MONTHLY --metrics UnblendedCost \
    --group-by Type=TAG,Key=team --query 'ResultsByTime[0].Groups[].[Keys[0],Metrics.UnblendedCost.Amount]' --output text | sort -k2 -nr
    ── expected output ──
    team$checkout 4120.44
    team$ 2980.17
    team$storefront 2210.09
    team$platform 1840.52
    team$catalog 912.30
  2. 2

    Set a budget with a forecast alert

    Alert when FORECAST spend will exceed the monthly budget, early enough to act.

    terminal
    $ aws budgets create-budget --account-id 123456789012 \
    --budget '{"BudgetName":"shoplite-prod-monthly","BudgetLimit":{"Amount":"12000","Unit":"USD"},"TimeUnit":"MONTHLY","BudgetType":"COST"}' \
    --notifications-with-subscribers '[{"Notification":{"NotificationType":"FORECASTED","ComparisonOperator":"GREATER_THAN","Threshold":100,"ThresholdType":"PERCENTAGE"},"Subscribers":[{"SubscriptionType":"SNS","Address":"arn:aws:sns:ap-south-1:123456789012:finops"}]}]'
  3. 3

    Cut NAT costs with VPC endpoints (Terraform)

    A free gateway endpoint for S3 and interface endpoints for ECR image pulls. Check NAT BytesOutToDestination in CloudWatch before and after.

    modules/network/endpoints.tfwhole filehcl
    resource "aws_vpc_endpoint" "s3" {
      vpc_id            = aws_vpc.this.id
      service_name      = "com.amazonaws.${var.region}.s3"
      vpc_endpoint_type = "Gateway"
      route_table_ids   = aws_route_table.private[*].id
    }
    
    resource "aws_vpc_endpoint" "interface" {
      for_each            = toset(["ecr.api", "ecr.dkr", "sts", "logs"])
      vpc_id              = aws_vpc.this.id
      service_name        = "com.amazonaws.${var.region}.${each.key}"
      vpc_endpoint_type   = "Interface"
      subnet_ids          = aws_subnet.private[*].id
      security_group_ids  = [aws_security_group.endpoints.id]
      private_dns_enabled = true
    }
  4. 4

    Lifecycle old logs and artifacts to cheaper storage

    lifecycle.jsonwhole filejson
    {
      "Rules": [
        { "ID": "logs-tiering", "Status": "Enabled", "Filter": { "Prefix": "logs/" },
          "Transitions": [ { "Days": 30, "StorageClass": "STANDARD_IA" }, { "Days": 90, "StorageClass": "GLACIER_IR" } ],
          "Expiration": { "Days": 365 } },
        { "ID": "abort-mpu", "Status": "Enabled", "Filter": {},
          "AbortIncompleteMultipartUpload": { "DaysAfterInitiation": 7 } }
      ]
    }
    terminal
    $ aws s3api put-bucket-lifecycle-configuration --bucket shoplite-logs-archive --lifecycle-configuration file://lifecycle.json

Operate it

Knobs that matter

SettingDefaultWhat it doesWhen to change it
Savings Plan commitmentnone$/hour you commit for 1 or 3 years.Cover the steady baseline (~60–80% of minimum usage); review quarterly.
Pod CPU/memory requestswhatever the team guessedWhat the scheduler reserves (and what you pay for).Set from observed p95 usage; use VPA recommendations.
CloudWatch Logs retentionNever expireHow long logs are kept.Set per log group (e.g. 30 days) and archive to S3 if needed longer.
Non-prod schedule24×7When dev/staging run.Scale to zero outside working hours with schedules or Karpenter limits.

3am practice

Failure drills

Each drill is a real failure mode. Read the scenario and the output, decide what's wrong, then reveal the diagnosis.

Drill #1

The NAT gateway costs more than the cluster

The monthly bill jumped by $3,000. EC2 is flat.

terminal
$ aws ce get-cost-and-usage --time-period Start=2026-09-01,End=2026-09-27 --granularity MONTHLY --metrics UnblendedCost --group-by Type=DIMENSION,Key=USAGE_TYPE --query 'ResultsByTime[0].Groups[?Metrics.UnblendedCost.Amount>`500`]' --output text
── what you'll see ──
APS3-NatGateway-Bytes 3412.80
APS3-BoxUsage:m7g.xlarge 2880.11

Drill #2

Idle but expensive

OpenCost shows __idle__ as 35% of cluster cost.

terminal
$ kubectl top nodes | head -4
── what you'll see ──
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
ip-10-20-11-14 610m 7% 5100Mi 16%
ip-10-20-12-87 890m 11% 6230Mi 19%
ip-10-20-13-5 420m 5% 3950Mi 12%

Decide

Ways to pay for compute on AWS

OptionDiscount vs on-demandCommitmentBest for
On-demand0%NoneSpiky, short-lived, unknown workloads
Compute Savings PlansUp to ~66%$/hr for 1–3 years, flexible across EC2/Fargate/LambdaSteady baseline across changing instance types
EC2 Instance Savings Plans / RIsUp to ~72%Family + region for 1–3 yearsVery stable fleets
SpotUp to ~90%None, but can be reclaimed with 2 min noticeStateless replicas, CI, batch, Karpenter pools

The bigger picture

Connects to

Prove it

Interview questions

01

How would you reduce a company's AWS bill?

02

Why can NAT gateways be expensive and how do you fix it?