Skip to content

FinOps

The first real invoice of BoxOffice's production environment arrives, and it is three times the estimate. Nobody can say why: the costs are spread over forty line items, half of them have names like USE1-NatGateway-Bytes and DataTransfer-Regional-Bytes, and no resource is tagged with the service that created it. In the cloud, every architectural decision is also a purchasing decision β€” and the bill is the one metric that every executive reads.

FinOps (cloud financial operations) is the practice of bringing financial accountability to the variable spending model of the cloud, so that engineering, finance and business make trade-offs between speed, cost and quality together1. For a system designer it comes down to three skills: estimate what an architecture will cost, attribute what it actually costs, and optimize without breaking the SLOs2.


The FinOps lifecycle

The FinOps Foundation organizes the practice in three phases that repeat continuously1:

flowchart LR
    inform["Inform<br/>visibility: tags, allocation,<br/>showback, unit costs"] --> optimize["Optimize<br/>rightsizing, commitments,<br/>spot, architecture changes"]
    optimize --> operate["Operate<br/>budgets, anomaly alerts,<br/>policies, reviews"]
    operate --> inform
  • Inform β€” you cannot optimize what you cannot see. Every resource carries tags (project, env, service, team); Kubernetes costs are split by namespace and label using the pods' resource requests (tools such as the CNCF project OpenCost4, or Kubecost). The result is showback (teams see their costs) or chargeback (teams are billed internally).
  • Optimize β€” reduce waste (idle and oversized resources) and pay less per unit (commitments, spot, cheaper architectures).
  • Operate β€” budgets with alerts, anomaly detection on daily spend, policies (no untagged resources, no GPU instances in dev), and a monthly cost review next to the SLO review.

Anatomy of a cloud bill

Cost type Billed by BoxOffice examples Watch out for
Compute vCPU-hour and GB-hour (VMs, containers), or request + GB-second (FaaS) Kubernetes nodes, Lambda thumbnails Idle replicas, oversized requests
Managed services Instance-hour + storage + I/O PostgreSQL, Redis, Kafka Multi-AZ doubles the instance; replicas add up
Storage GB-month, plus requests S3 ticket PDFs, database volumes, snapshots Snapshots and logs kept forever
Data transfer GB moved CDN egress to fans, cross-AZ traffic between services, NAT processing Internet egress and cross-AZ traffic are charged; ingress usually is not
Fixed hourly items Hour, whether used or not Kubernetes control plane, load balancers, NAT gateways, public IPv4 addresses They dominate small and non-production environments
Observability GB ingested, retained, queried; active series Logs, metrics, traces High-cardinality metrics and debug logs in production
Third-party SaaS Per transaction or per seat Payment fees (a percentage of the ticket price), e-mail per thousand Often larger than the whole infrastructure β€” but a business cost

Pricing models

Model Discount vs on-demand Commitment Good for
On-demand β€” None Spiky and unknown workloads; the reference price
Commitments (AWS Savings Plans / Reserved Instances, GCP committed use discounts, Azure reservations) Substantial (often tens of percent) for 1 or 3 years Pay for a baseline whether you use it or not The steady baseline: the 6 always-on nodes, the databases
Spot / preemptible Large, variable None, but the provider can reclaim the capacity with short notice (two minutes on AWS) Stateless, interruptible work: workers, batch, CI runners, part of the stateless fleet
Serverless / per request β€” None Low or very spiky traffic; scale to zero

The usual portfolio is commit for the floor, on-demand for the daily wave, spot for the interruptible part β€” which only works if you know your baseline, i.e., after the Inform phase.


A cost model of BoxOffice

The model below estimates the monthly bill of the production architecture designed in this course. Every number is an assumption you can change β€” that is the point of a model: it makes the drivers explicit and lets you ask "what if?". Prices are approximate on-demand list prices for AWS us-east-1; confirm them with the provider's calculator before quoting them, and remember that other regions (including sa-east-1, SΓ£o Paulo) are more expensive.

Assumptions

  • Production only (staging and load-test environments are extra), running 24/7 (730 h per month).
  • Steady state: 6 worker nodes run all services. Four on-sales per month, each needing 24 extra nodes for 2 hours.
  • Not included: payment-provider fees, taxes, support plans, free tiers and discounts, cross-AZ transfer between services.
ComponentPaletteSizing (assumption)Steady $/monthOn-sale extraShare
Kubernetes control planeβ€”1 cluster$73$01%
Worker nodes (services)app6 Γ— m6i.xlarge, +24 at peak$841$3717%
PostgreSQL primary, Multi-AZsqldb.r6g.large Γ—2 + 2 Γ— 200 GB$374$07%
PostgreSQL read replicasqldb.r6g.large + 200 GB$187$04%
Redis (primary + replica)cache2 Γ— cache.r6g.large$301$06%
Kafka (managed)kafka3 brokers + 3 Γ— 500 GB$610$012%
Load balancerlb1 ALB, ~5 LCU (Γ—20 at peak)$46$61%
NAT gatewaysnat3 AZs + 2 TB processed$189$04%
Object storageobjstore500 GB tickets and images$12$00%
CDN egresscdn20 TB/month$1,700$034%
Logs ingestionlogs1 TB/month$500$010%
Serverless thumbnailsserverless2 M runs Γ— 0.5 s Γ— 1 GB$17$00%
Transactional e-mailemail1.5 M e-mails$150$03%
Total$4,999$43100%

Total β‰ˆ $5,042/month. The four on-sales add only $43 because the 24 extra nodes exist for 8 hours; keeping them 24/7 'just in case' would add β‰ˆ $3,364/month. At 400,000 tickets per month the platform costs β‰ˆ $0.013 per ticket sold.

2026-11-01T21:54:23.070886 image/svg+xml Matplotlib v3.11.2, https://matplotlib.org/

Reading the model

  • The peak is cheap; the baseline is not. Elastic capacity for the on-sales costs almost nothing because it exists for eight hours. Provisioning for the peak permanently would cost more than the entire rest of the compute bill. This is the economic argument for autoscaling, pre-scaling and waiting rooms (Capacity planning).
  • Data services dominate compute. PostgreSQL, Redis and Kafka together cost more than the nodes running nine services. Before adding a fourth data store, ask whether an existing one can do the job.
  • Bytes cost more than CPUs. CDN egress and log ingestion are two of the largest lines. Image compression, longer cache lifetimes, sampling debug logs and shorter retention change the bill more than tuning Java.
  • Unit economics turns the bill into a business metric: cost per ticket sold (or per 1 000 requests, per active user). A bill that doubles while tickets triple is good news; a bill that grows while tickets are flat is a regression β€” track it like a performance regression.

Pay per request or pay per hour?

When is FaaS cheaper than always-on containers? The plot compares a function (100 ms per request at 512 MB, behind an HTTP API front door) with a minimum of two small container tasks behind a load balancer, each serving 50 requests per second:

2026-11-01T21:54:23.261885 image/svg+xml Matplotlib v3.11.2, https://matplotlib.org/

Below roughly ten requests per second on average, paying per request wins β€” idle containers are pure waste. Above it, the per-request premium accumulates and always-on containers win, more so with commitments. Spiky traffic shifts the picture: what matters is the average, while containers must be sized for the peak (or autoscale). This is why BoxOffice runs the thumbnail generator and webhook receivers as functions and the purchase path as containers.


Optimization levers

The levers below follow the cost-optimization practices of the cloud providers' well-architected frameworks3. Each one saves money and each one, except the last, can hurt an SLO if applied blindly.

Lever What to do Typical effect Risk to the SLOs
Rightsizing Set requests from measured usage (e.g. p95 of CPU and max of memory over two weeks), not guesses Fewer nodes for the same pods Too tight β†’ throttling and OOMKilled; re-run the load test
Autoscaling and scale to zero HPA + node autoscaling; KEDA scale-to-zero for workers; turn off staging at night and weekends Non-production hours: 730 β†’ ~250 per month Cold starts; scale-up lag at peaks
Commitments Cover the measured baseline with 1-year commitments Lower unit price on the floor Paying for unused commitment if the architecture changes
Spot capacity Run workers, CI and a share of stateless replicas on spot, spread across instance types Large discount on that share Interruptions: needs graceful shutdown, PDBs, on-demand fallback
ARM instances Graviton/Axion/Ampere nodes for JVM and Python services (multi-arch images) Lower price per vCPU for similar performance on many workloads Verify with a benchmark; native libraries must exist for ARM
Reduce data transfer CDN with long TTLs; keep chatty services in the same AZ; VPC endpoints for S3/ECR instead of NAT Lower egress and NAT processing Zone affinity reduces spreading
Storage lifecycle Move old ticket PDFs to infrequent-access/archive tiers; expire snapshots Lower GB-month Slower, priced retrieval
Observability hygiene Sample traces, drop debug logs in production, cap metric cardinality, shorter retention Lower ingestion bill Less detail when debugging β€” keep exemplars and error traces
Delete idle resources Unattached volumes, old load balancers, forgotten test clusters Pure savings None

Cost and reliability pull in opposite directions

Multi-AZ doubles the database cost; a second region roughly doubles everything. Those are not waste β€” they buy availability. FinOps is not "spend less", it is "know what each dollar buys". Write the trade-off in the design document: "Multi-AZ PostgreSQL costs $187/month more and turns a zone failure from a multi-hour outage into a one-minute failover."


Estimating in a system-design interview

Interviewers rarely want dollars, but cost-awareness is a strong signal. A three-step habit is enough:

  1. Name the drivers: "Cost is dominated by egress for images (CDN), the data tier, and peak compute."
  2. Order of magnitude from your capacity estimate: "30 nodes for 8 hours a month is cheap; 30 nodes for 730 hours is not β€” so we autoscale and pre-scale."
  3. One optimization with its trade-off: "Spot for workers, because they are idempotent and retry; never for the primary database."

In the interview

Cost questions often come disguised: "Do we really need Kafka here?", "Why three replicas?", "Could this be serverless?". Answer with utilization and peak-to-average ratio: always-on capacity is cheapest when it is busy; per-request pricing is cheapest when traffic is low or very spiky. Mention that data transfer and managed data stores usually dominate before compute does.

Best practices

  • Allocate the cluster bill by resource requests, per namespace and label β€” nodes are shared, so requests are the fair key, and they are what the scheduler reserves.
  • Set a budget with alerts at 50%, 80% and 100%, plus an anomaly alert on daily spend β€” a forgotten load-test cluster shows up in a day, not on the invoice.
  • Put the cost of a design change in its design document, in dollars per month β€” "Multi-AZ PostgreSQL: +$187/month, a zone failure becomes a one-minute failover" lets the reviewer judge the trade-off. In BoxOffice: optimization levers.
  • Commit only up to the lowest usage of the last months β€” commitments are paid whether used or not; the floor is the only safe part. In BoxOffice: the 6 always-on nodes, not the 24 on-sale nodes (cost model).
  • Give each telemetry signal its own retention β€” error traces and SLO metrics for months, debug logs for days; ingestion and retention are among the largest lines of the bill.

Pitfalls

  • No tags: costs that cannot be attributed cannot be owned or optimized.
  • Estimating compute only: the fixed hourly items, data transfer and observability are where surprises come from.
  • Rightsizing without a load test: the savings show up in the bill, the throttling shows up in the p99 at the next peak.
  • Commitments before measuring the baseline: locking in capacity for an architecture you are about to change.
  • Non-production environments running 24/7: often a large share of the total bill.
  • Optimizing cents while the NAT gateway processes terabytes: sort by cost before optimizing.

How it connects

flowchart LR
    BILL["the bill: compute, managed services,<br/>storage, transfer, fixed items"] --> INF["Inform: tags,<br/>cost by pod requests"]
    INF --> OPT["Optimize: rightsizing,<br/>commitments, spot, ARM"]
    OPT --> OPS["Operate: budgets,<br/>anomalies, monthly review"]
    BASE["baseline vs peak<br/>β†’ autoscaling, pre-scaling"] --> OPT
    FAAS["per request vs per hour<br/>β†’ serverless break-even"] --> OPT
    UNIT["unit economics:<br/>cost per ticket sold"] --> OPS
    OPT --> SLO["every lever can hurt an SLO<br/>β†’ Reliability"]
    OPT --> LT["re-run the load test<br/>β†’ Performance"]
    BILL --> MAZ["Multi-AZ, second region:<br/>cost that buys availability"]
    BILL --> OBS["observability bill:<br/>cardinality, retention"]

Palette components: sql Β· cache Β· kafka (the data tier dominates compute) Β· cdn (egress) Β· nat (fixed hourly + per GB processed) Β· objstore (lifecycle tiers) Β· logs (ingestion) Β· serverless Β· worker (spot candidates) Β· payment (fees, a business cost). Scenarios: serverless-burst, multi-region (what a second region costs). Continues in: Compute β€” containers or serverless, Performance β€” capacity planning, Reliability β€” disaster recovery, Network & security β€” the cloud network (NAT gateways), Observability β€” signals (cardinality), Project β€” cost report.



  1. FinOps Foundation. FinOps Framework. ↩↩

  2. STORMENT, J. R.; FULLER, M. Cloud FinOps. 2nd ed. O'Reilly, 2023. ↩

  3. AWS. Well-Architected Framework β€” Cost Optimization Pillar. ↩

  4. OpenCost. Open source cost monitoring for cloud-native environments (CNCF project). ↩