This guide was written by Arthur Azrieli, CTO at MeteorOps, and first published on the MeteorOps blog.
The way to reduce cloud costs without slowing engineering is to take the decisions off engineers' plates. Make waste disappear through defaults and automation (lifecycle rules, log retention, after-hours schedules, environment TTLs), buy discounts on the stable baseline you already run, and show each team its own spend where it already works: in pull requests and in a weekly per-team report. Do not add approval gates, ticket queues, or a monthly "cost review" meeting that pulls engineers out of delivery.
In practice that means a specific order. First, get spend attributed to teams and kill obvious waste (idle resources, forgotten non-production environments, unbounded log retention). Second, rightsize and fix the platform defaults that create the waste (Kubernetes requests, node autoscaling, storage classes, NAT gateway traffic). Third, commit to Savings Plans once the baseline is clean. Fourth, put guardrails in code so the bill does not drift back.
This playbook is written for CTOs, VPs of Engineering, and platform or DevOps leads at startups and scale-ups (roughly 10 to 200 engineers, mostly on AWS, often on Kubernetes) whose cloud bill is growing faster than revenue. Every AWS fact below links to AWS documentation; where we give an opinion, it comes from running these changes in production for teams like yours.
Why cost work usually slows engineering (and how to avoid it)
Cost initiatives slow teams down when they turn spending into a permission problem. The common failure patterns are familiar:
Approval gates. A ticket to request a bigger instance or a new environment. Engineers wait, then over-provision when they finally get approval so they never have to ask again.
Finance-shaped dashboards. A chart that says "EC2 is up 18%" gives nobody a decision to make. The engineer still has to dig through clusters, accounts, and services to find the owner.
One-off cleanup sprints. A team spends two weeks deleting things, the bill drops, and six months later it is back because nothing changed in how resources are created.
Risky savings. Aggressive rightsizing or Spot adoption without the reliability groundwork causes an incident, and the organization learns that "cost work breaks production."
The alternative is to treat cost like any other non-functional requirement: encode it in platform defaults, measure it per service, and give owners fast feedback. The FinOps Foundation describes the cycle as three phases, Inform, Optimize, and Operate: see the data, find improvements, then make them part of how you run. The rest of this guide follows that cycle with concrete AWS and Kubernetes steps.
The 30/60/90 day plan to reduce cloud costs
Here is the sequence we recommend. It front-loads changes that need no product decisions and no engineer time from feature teams.
| Phase | Goal | Actions | Who does the work |
|---|---|---|---|
| Days 1 to 30 | Visibility and zero-risk waste | Activate cost allocation tags, turn on Cost Anomaly Detection and budgets, delete unattached volumes and old snapshots, set log retention, migrate gp2 to gp3, schedule non-prod off after hours | Platform team (1 to 2 people) |
| Days 31 to 60 | Fix the defaults that create waste | Rightsize Kubernetes requests, enable node consolidation, add Spot for tolerant workloads, add S3 gateway endpoints, add lifecycle rules, cut log and metric volume | Platform team, with service owners reviewing requests |
| Days 61 to 90 | Lock in rates and prevent drift | Buy Savings Plans on the cleaned baseline, add cost estimates to pull requests, enforce tags and TTLs with policy as code, start per-team showback | Platform team plus engineering leadership |
The order matters. If you buy commitments first, you lock in a discount on waste you were about to delete. If you add guardrails before visibility, nobody knows why the guardrail exists.

Commit to discounts only after the waste is gone, then lock in the result with guardrails.
Days 1 to 30: visibility and quick wins
Make spend attributable to an owner
Unowned spend becomes permanent spend. Before anything else, every dollar should map to a team and a service. The two strongest tools are account structure and tags.
Separate accounts per environment (at minimum production and non-production) through AWS Organizations. Account boundaries are the most reliable cost split because they need no tagging discipline.
A small, mandatory tag set. Keep it to three or four keys, for example
team,service,env, and optionallyttlfor ephemeral resources. More keys means lower compliance.Activate the tags for billing. User-defined tags do not appear in billing data until you activate them as cost allocation tags. AWS notes it can take up to 24 hours for tag keys to appear and up to another 24 hours to activate, so do this on day one.
Kubernetes needs its own allocation. On EKS, the bill shows EC2 nodes, not pods. AWS offers split cost allocation data for EKS in its billing data, and Kubecost or OpenCost (a CNCF incubating project) break costs down by namespace, workload, and label.
Tag in Terraform with provider-level default_tags so engineers do not have to remember:
provider "aws" {
region = "us-east-1"
default_tags {
tags = {
team = "payments"
service = "checkout-api"
env = "staging"
}
}
}
Turn on anomaly detection and budgets
The cheapest overspend is the one you catch on day two instead of at the end of the month. AWS Cost Anomaly Detection uses machine learning models on your net unblended cost, runs roughly three times a day, and can alert by email or SNS (which you can route to Slack). Expect a delay: it relies on Cost Explorer data that can lag up to 24 hours. Set it up in Terraform so it lives next to the rest of your infrastructure:
resource "aws_ce_anomaly_monitor" "services" {
name = "per-service-monitor"
monitor_type = "DIMENSIONAL"
monitor_dimension = "SERVICE"
}
resource "aws_ce_anomaly_subscription" "daily" {
name = "cost-anomalies-daily"
frequency = "DAILY"
monitor_arn_list = [aws_ce_anomaly_monitor.services.arn]
subscriber {
type = "EMAIL"
address = "platform-team@example.com"
}
threshold_expression {
dimension {
key = "ANOMALY_TOTAL_IMPACT_ABSOLUTE"
match_options = ["GREATER_THAN_OR_EQUAL"]
values = ["100"]
}
}
}
Add AWS Budgets on each account with forecast-based alerts. Budgets can also run budget actions such as applying an IAM policy or SCP, or stopping specific EC2 and RDS instances. Use actions for sandbox accounts only; on production, alerts should go to a human who owns the service.
Delete idle resources
Idle resources are the safest savings because nobody uses them. AWS Cost Optimization Hub consolidates idle-resource, rightsizing, Savings Plans, and Reserved Instance recommendations across accounts and Regions, so start there. Then check the usual suspects directly:
# Unattached EBS volumes (paying for storage nobody mounts)
aws ec2 describe-volumes \
--filters Name=status,Values=available \
--query 'Volumes[].{ID:VolumeId,SizeGiB:Size,Created:CreateTime}' \
--output table
# Elastic IPs not associated with anything
aws ec2 describe-addresses \
--query 'Addresses[?AssociationId==`null`].PublicIp' \
--output table
# CloudWatch log groups with no retention (kept forever by default)
aws logs describe-log-groups \
--query 'logGroups[?!retentionInDays].[logGroupName,storedBytes]' \
--output table
Also look for old EBS snapshots and AMIs from past migrations, load balancers with no healthy targets, idle RDS instances in non-production, and NAT gateways in VPCs nobody uses. Snapshot before you delete anything you are not sure about; snapshots are cheap insurance compared with a surprise outage.
Set log retention and switch gp2 to gp3
Two changes that are almost always safe:
Log retention. CloudWatch log groups keep data indefinitely unless you set a retention period. Pick a default (for example 30 days for application logs, longer only where compliance requires it) and apply it with
aws logs put-retention-policy --log-group-name NAME --retention-in-days 30or in Terraform.gp2 to gp3. AWS states gp3 volumes cost up to 20% less per GB than gp2 and deliver a baseline of 3,000 IOPS and 125 MB/s at any size. The migration is an online volume modification:
aws ec2 modify-volume --volume-id vol-0123456789abcdef0 --volume-type gp3. Check volumes that need more than the gp3 baseline before converting them, and update your Kubernetes StorageClass so new volumes default to gp3.
Turn non-production off after hours
Development and staging environments that run 24/7 but are used 50 hours a week are pure waste. AWS's own Instance Scheduler on AWS starts and stops tagged EC2 and RDS instances on a schedule, and AWS cites up to 70% savings for instances only needed during business hours (168 weekly hours reduced to 50). On Kubernetes, scale workloads to zero with a KEDA cron trigger and let the node autoscaler remove the empty nodes:
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: checkout-api-business-hours
namespace: staging
spec:
scaleTargetRef:
name: checkout-api
minReplicaCount: 0
triggers:
- type: cron
metadata:
timezone: Europe/London
start: "0 7 * * 1-5"
end: "0 20 * * 1-5"
desiredReplicas: "2"
Make the schedule opt-out, not opt-in, and give engineers a one-command way to wake an environment (a Slack command or a CI job). The guardrail that does not slow anyone down is the one with an easy override.
Days 31 to 60: rightsizing and platform defaults
Rightsize Kubernetes requests and enable bin-packing
On Kubernetes, you pay for nodes, and nodes are sized by the sum of pod requests, not actual usage. If every service requests 1 CPU and uses 0.1, your cluster is roughly 90% air no matter how good your autoscaler is. This is usually the single biggest lever for Kubernetes cost optimization. (If the difference between requests and limits is fuzzy, start with our guide to Kubernetes resource requests and limits.)

You pay for what pods request, so savings appear only when requests shrink and nodes are removed.
Measure. Compare requested CPU and memory with actual p95 usage per workload, using Prometheus, Kubecost, or OpenCost.
Get recommendations without risk. Run the Vertical Pod Autoscaler in recommendation-only mode so it suggests requests but never restarts pods:
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: checkout-api
namespace: production
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: checkout-api
updatePolicy:
updateMode: "Off"
Apply through pull requests to each service's manifests or Helm values, reviewed by the owner. Lower CPU requests are low risk (CPU is compressible); be more careful with memory, because a memory limit that is too low means OOM kills.
Let the cluster shrink. Rightsized requests only save money if nodes are removed. With Karpenter, consolidation does this:
WhenEmptyOrUnderutilized(the default) replaces or removes nodes whose pods fit elsewhere, and disruption budgets cap how many nodes move at once.
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: general
spec:
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized
consolidateAfter: 5m
budgets:
- nodes: "10%"
Consolidation moves pods, so it is only safe when your workloads tolerate it: PodDisruptionBudgets, multiple replicas, and correct readiness probes. Our guides on draining nodes without evicting critical workloads and Kubernetes autoscaling without cost surprises cover that groundwork.
Use Spot where interruptions are cheap
AWS advertises Spot Instances at up to 90% off On-Demand prices. The trade-off is interruption: EC2 issues an interruption notice two minutes before it stops or terminates a Spot Instance, on a best-effort basis. Good candidates are CI runners, batch and queue workers, stateless services with several replicas, and most non-production environments. Poor candidates are single-replica stateful services and anything that takes longer than two minutes to shut down cleanly. Diversify across many instance types and Availability Zones so a single capacity pool going away does not take all your nodes with it, and keep an On-Demand floor for critical services.
Rightsize instances and databases, and try Graviton
Outside Kubernetes, use AWS Compute Optimizer (surfaced in Cost Optimization Hub) to find EC2, RDS, Lambda, and Fargate resources that are larger than their measured usage. Then consider Graviton: AWS states Graviton-based instances cost up to 20% less than comparable x86-based EC2 instances. For interpreted languages (Node.js, Python, Ruby) and the JVM, the move is often just a multi-architecture container build plus a node pool change. Native dependencies and third-party agents are what usually block it, so test one service end to end before planning the fleet.
Cut data transfer and NAT gateway costs
Data transfer is the line item teams understand least. The most common surprise is the NAT gateway. AWS charges an hourly charge per NAT gateway plus a per-GB data processing charge for every gigabyte processed through it, regardless of source or destination (AWS's example rate in US East (Ohio) is $0.045 per hour and $0.045 per GB). So a private-subnet service pulling container images or reading S3 through a NAT gateway pays a processing fee on every byte.
Add gateway VPC endpoints for S3 and DynamoDB. AWS states there are no data processing or hourly charges for gateway-type VPC endpoints, and S3 traffic then bypasses the NAT gateway. This is usually the highest return-on-effort change in the whole network.
Use a pull-through cache or ECR in the same Region for container images rather than pulling from public registries through NAT on every node start.
Watch cross-AZ traffic. Traffic between Availability Zones carries data transfer charges. Chatty services and replicas spread across zones can generate a surprising bill. Check the
DataTransfer-Regional-Bytesusage types in Cost Explorer before optimizing, so you fix what actually costs money.

A gateway endpoint takes S3 traffic off the NAT gateway and its per-GB processing fee.
Storage tiers and lifecycle policies
S3 buckets that only grow are a slow leak. Apply lifecycle rules to anything with a predictable access pattern (logs, backups, build artifacts) and use S3 Intelligent-Tiering where access is unpredictable. Intelligent-Tiering moves objects to an Infrequent Access tier after 30 days without access and to Archive Instant Access after 90, for a small per-object monitoring charge; objects under 128 KB are not monitored or tiered, so it does little for buckets full of tiny files.
resource "aws_s3_bucket_lifecycle_configuration" "logs" {
bucket = aws_s3_bucket.logs.id
rule {
id = "logs-tiering-and-expiry"
status = "Enabled"
filter {
prefix = "logs/"
}
transition {
days = 30
storage_class = "STANDARD_IA"
}
expiration {
days = 365
}
abort_incomplete_multipart_upload {
days_after_initiation = 7
}
}
}
Reduce observability and log costs without losing visibility
Observability is often the second or third largest line on a startup's bill once you add the vendor invoice (Datadog, New Relic, Grafana Cloud, or CloudWatch itself). The goal is not less visibility; it is paying only for data someone will actually look at. Work through these in order:
Find the top emitters. In most systems a handful of services and log lines produce most of the volume: health checks, debug logs left on, per-request access logs, and retry loops. Rank by volume per service and per log pattern before changing anything.
Drop or demote noise at the source. Set production log levels to INFO or WARN, stop logging successful health checks, and sample high-volume success logs (for example keep 1 in 100 successful requests, all errors).
Filter before indexing. Most vendors let you ingest without indexing. In Datadog, exclusion filters discard logs from indexes while still letting them feed log-based metrics and archives. Convert "count of X" log searches into metrics, which are far cheaper to keep.
Tier by access. CloudWatch's Infrequent Access log class has a lower ingestion price per GB but drops features such as Live Tail, metric filters, and subscription filters, and a log group's class cannot be changed after creation. Use it for audit and forensic logs you query rarely, and archive to S3 for long-term retention.
Sample traces intelligently. Tail sampling keeps every error and slow trace while sampling the boring ones. With the OpenTelemetry Collector:
processors:
tail_sampling:
decision_wait: 10s
policies:
- name: keep-errors
type: status_code
status_code:
status_codes: [ERROR]
- name: keep-slow
type: latency
latency:
threshold_ms: 2000
- name: sample-the-rest
type: probabilistic
probabilistic:
sampling_percentage: 10
- Control metric cardinality. Custom metrics tagged with user IDs, request IDs, or pod names explode series counts. Drop high-cardinality labels in the collector or agent before they reach the vendor.
To prove you did not lose visibility, keep a short list of the queries and alerts your on-call actually used in the last quarter and confirm each one still works after every change.
Days 61 to 90: Savings Plans vs Reserved Instances vs Spot
Commitments are the largest discount available and the easiest to get wrong. Buy them last, on the baseline that remains after cleanup and rightsizing. AWS defines four Savings Plans types, all with 1-year or 3-year terms and All Upfront, Partial Upfront, or No Upfront payment:
| Option | Maximum discount (per AWS) | Flexibility | Best for |
|---|---|---|---|
| Compute Savings Plans | Up to 66% off On-Demand | Any EC2 family, size, Region, OS; also Fargate and Lambda | Most startups; teams still changing instance types, moving to Graviton, or shifting to Fargate |
| EC2 Instance Savings Plans | Up to 72% off On-Demand | One instance family in one Region; any size within it | Very stable fleets you will not migrate during the term |
| Database Savings Plans | Up to 35% | Aurora, RDS, DynamoDB, ElastiCache, DocumentDB, OpenSearch and others, across engines and Regions | Steady database spend, especially if you may change engines or instance generations |
| Reserved Instances | Varies by service and term | Tied to specific attributes | Services not covered by a Savings Plan you need, or capacity reservations |
| Spot | Up to 90% off On-Demand | Can be interrupted with a two-minute notice | Interruption-tolerant workloads; not a commitment at all |
Our guidance for a growing startup:
Prefer Compute Savings Plans. You give up a few percentage points versus EC2 Instance plans and keep the freedom to change architecture. For a team that is still evolving, that freedom is worth more.
Cover the floor, not the average. Commit to the lowest sustained hourly compute spend over recent months, not the average. Under-committing costs you a small missed discount; over-committing costs you money for nothing, and AWS states the terms cannot be changed after purchase.
Ladder purchases. Buy smaller plans every quarter rather than one large plan, so expiries are staggered and you can adjust as you grow.
Know what is not covered. On EKS, Savings Plans cover the underlying EC2 instances but not the EKS cluster charge itself.

Commit to the lowest sustained spend so every committed dollar is actually used.
Cost Explorer generates Savings Plans purchase recommendations from your history; treat them as a starting point and adjust for anything you know is about to change (a migration, a big customer, a planned Graviton move).
Guardrails that do not slow engineers down
A good guardrail is automatic, explains itself, and has an obvious escape hatch. A bad one is a ticket. Here are the guardrails we install, roughly in order of value:
| Guardrail | How to implement | Why it does not slow anyone |
|---|---|---|
| Default tags | Terraform provider default_tags, Kubernetes labels set in shared Helm charts |
Engineers get tagging for free |
| TTLs on ephemeral environments | A ttl tag or label plus a scheduled cleanup job; preview environments destroyed on PR merge or close |
Nobody has to remember to clean up |
| Auto-stop non-prod after hours | Instance Scheduler on AWS, KEDA cron, or scaling node groups to zero | Opt-out tag and a wake-up command |
| Policy as code in audit mode first | Kyverno or OPA Gatekeeper in Kubernetes, tag policies and SCPs in AWS Organizations | Warns before it blocks; blocks only what is clearly wrong |
| Cost estimate in every infrastructure PR | Infracost or similar in CI | Feedback arrives in the review the engineer is already doing |
| Anomaly alerts routed to owners | Cost Anomaly Detection per account or cost category, sent to the owning team's channel | Only fires when something is actually unusual |
| Sandbox budget actions | AWS Budgets actions applying an SCP when a sandbox account exceeds its budget | Applies only to experiments, never production |
Start policies in audit mode, look at what they would have blocked, fix the false positives, then enforce. A Kyverno policy that requires a team label on Deployments, in audit mode, looks like this:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-team-label
spec:
background: true
rules:
- name: check-team-label
match:
any:
- resources:
kinds:
- Deployment
validate:
failureAction: Audit
message: "Every Deployment needs a team label for cost allocation."
pattern:
metadata:
labels:
team: "?*"
The biggest guardrail of all is a self-service path that is cheap by default. If spinning up an environment through your platform gives you right-sized, scheduled, tagged, TTL-bound resources automatically, engineers will use it because it is faster, and cost control comes along for free. That is the idea behind a one-click environment.
How to give engineers cost visibility in their workflow
Engineers care about cost when they can see the effect of their own changes, quickly, in a place they already look. Three layers work well together:
Before deploy: cost in the pull request. Infracost estimates costs from Terraform, CloudFormation, and AWS CDK and can comment on each pull request with the cost difference, policy violations, and tagging issues introduced by the change, with integrations for GitHub Actions, GitLab CI, and Azure DevOps. A reviewer seeing "+$1,400/month" next to a database instance change is the cheapest cost control you will ever add.
After deploy: per-team and per-service dashboards. Built from your cost allocation tags (Cost Explorer, Cost Categories, or a CUR-based dashboard) and, for Kubernetes, from Kubecost or OpenCost by namespace. The test of a good dashboard: can a team lead answer "what did my services cost last week and why did it change" in under a minute?
Weekly: a short showback message. A Slack post per team with last week's spend, the change versus the prior week, and the top three movers. No action required unless something looks wrong. Showback (reporting cost to owners) builds the habit without the overhead of chargeback (billing it to their budget).

Engineers see cost at review time, after release, and in a weekly summary, without leaving their workflow.
Where possible, express cost as a unit metric the team cares about: cost per 1,000 requests, per active customer, per build, or per GB processed. "Our bill went up 20%" is alarming; "cost per customer went down 8% while customers grew 30%" is a success, and a unit metric tells you which one you are looking at.
How to get engineering buy-in for cost reduction
Most engineers do not resist saving money. They resist being blamed, being slowed down, and being handed toil. Get buy-in by addressing those directly:
Frame it as engineering quality, not frugality. Over-provisioned requests, unbounded logs, and forgotten environments are bugs in how the system is run. Fixing them is the same craft as fixing a slow query.
The platform team does the first round. Quick wins in the first 30 days should cost feature teams zero hours. Showing a real reduction before asking anyone for anything earns the right to ask later.
Give ownership, not orders. Each team sees its own spend and decides how to act. Leadership sets a target (for example, a unit cost target or a cap on cloud spend as a percentage of revenue), not a list of instance types.
Never trade reliability for savings silently. Every change that touches production capacity (Spot, consolidation, lower requests) goes through the normal review and rollout process. One cost-driven outage will kill the program.
Celebrate the wins publicly. Share savings in engineering all-hands with the name of the team that found them. It costs nothing and turns cost into something people want to be associated with.
If you are an engineering leader structuring this for the first time, our CTO DevOps handbook covers how to set ownership boundaries between platform and product teams.
Cloud cost optimization tools by category
You do not need a paid platform to start. AWS native tools cover the first 90 days for most startups; third-party tools earn their place when you need Kubernetes allocation, multi-cloud views, or engineer-facing workflows. Capabilities below are as described in each product's own documentation; check current pricing directly with the vendor.
| Category | AWS native | Third-party or open source | Use it for |
|---|---|---|---|
| Spend analysis and allocation | Cost Explorer, cost allocation tags, Cost Categories, Cost and Usage Reports | CloudZero, Vantage, Kubecost, OpenCost | Who spends what, and unit costs |
| Recommendations | Cost Optimization Hub, Compute Optimizer, Trusted Advisor | Most commercial FinOps platforms | Idle resources, rightsizing, commitments |
| Alerts and budgets | Cost Anomaly Detection, AWS Budgets and budget actions | Commercial platforms with Slack routing | Catching spikes in days, not at month end |
| Kubernetes cost | Split cost allocation data for EKS | Kubecost, OpenCost; Karpenter for consolidation | Namespace and workload cost, bin-packing |
| Cost in the developer workflow | None built in | Infracost | Cost diffs on pull requests |
| Scheduling non-prod | Instance Scheduler on AWS | KEDA cron scaler, kube-downscaler | Turning environments off after hours |
| Policy and governance | Tag policies, SCPs in AWS Organizations, AWS Config rules | Kyverno, OPA Gatekeeper, Cloud Custodian | Enforcing tags, TTLs, and allowed resource types |
| Observability cost | CloudWatch log classes, retention settings | OpenTelemetry Collector (sampling, filtering), vendor exclusion filters | Reducing log, metric, and trace volume |
When you evaluate a paid cost platform for a team that is mostly engineers, ask three questions. Does it attribute Kubernetes spend to teams without a tagging project? Does it deliver insights where engineers already work (pull requests, Slack, the IDE) rather than in another dashboard? And does it need agents or ongoing maintenance from your platform team? A tool that adds operational overhead is just another cost line.
Common cloud cost reduction mistakes
Buying Savings Plans before cleanup. You lock in a discount on waste. Clean up and rightsize first, then commit to the floor.
Rightsizing nodes instead of requests. On Kubernetes, smaller nodes do nothing if pod requests stay inflated. Fix requests, then let consolidation shrink the fleet.
Setting memory limits too tight. CPU over-request is cheap to fix; memory under-provisioning causes OOM kills and restarts. Lower memory requests gradually and watch.
Moving everything to Spot at once. Without PodDisruptionBudgets, multiple replicas, and graceful shutdown, Spot interruptions become incidents.
Ignoring the NAT gateway. Teams optimize compute for weeks while S3 and image traffic quietly pay per-GB processing fees. Add gateway endpoints early.
Cutting logs blindly. Dropping logs without checking which queries and alerts depend on them saves money until the next incident. Inventory what on-call uses first.
Too many mandatory tags. Ten required tag keys means nobody complies. Three or four, applied by default, beats ten applied by hand.
Making it a project instead of a habit. A one-time cleanup decays. Anomaly alerts, PR cost diffs, and weekly showback keep it from coming back.
Cloud cost reduction checklist
Visibility
Production and non-production in separate AWS accounts
3 to 4 mandatory tags applied through
default_tagsand shared Helm chartsCost allocation tags activated in the Billing console
Kubernetes cost allocation by namespace (split cost allocation data, Kubecost, or OpenCost)
Cost Anomaly Detection and budgets on every account, routed to owners
Quick wins
Unattached EBS volumes, unused Elastic IPs, and old snapshots removed
Retention set on every CloudWatch log group
gp2 volumes migrated to gp3; StorageClass defaults to gp3
Non-production scheduled off outside working hours
S3 and DynamoDB gateway endpoints in every VPC with a NAT gateway
Structural changes
Pod requests rightsized from measured usage; VPA in recommendation mode
Node consolidation enabled with disruption budgets and PDBs in place
Spot used for CI, batch, and stateless multi-replica services
S3 lifecycle rules on log, backup, and artifact buckets
Log levels, sampling, and exclusion filters reviewed for the top emitters
Compute Savings Plans covering the sustained floor, purchased in quarterly ladders
Guardrails
Cost diff on every infrastructure pull request
TTLs on preview and ephemeral environments
Policy as code for tags, running in audit mode before enforce
Weekly per-team showback with unit cost trends
FAQ: reducing cloud spend without slowing engineering
How do engineering teams reduce cloud spend without slowing delivery?
They move cost control into defaults and automation instead of approvals. The platform team removes waste (idle resources, log retention, after-hours schedules), rightsizes Kubernetes requests, and buys Savings Plans on the cleaned baseline, while feature teams only see a cost diff in their pull requests and a weekly report on their own services. Nobody waits on a ticket to ship.
How do engineering teams reduce log volume and cost without losing visibility?
Start by ranking log volume by service and pattern, because a few sources usually dominate. Drop health-check and debug noise, sample high-volume success logs while keeping all errors, convert count-style log searches into metrics, and use tail sampling for traces. Before and after each change, confirm the queries and alerts on-call actually used still work.
How do I create FinOps guardrails that don't slow down engineers?
Make every guardrail automatic and give it an escape hatch. Examples: default tags in Terraform, TTLs on preview environments, non-production that auto-stops after hours with a one-command wake-up, policies that run in audit mode before they enforce, and anomaly alerts that only fire on unusual spend. Avoid manual approval steps for routine resources.
How do I get developers to care about cloud costs without making it a burden?
Show them the cost of their own changes, in tools they already use, and do not blame them for the past. A cost estimate in the pull request and a weekly per-team spend summary create awareness with no extra work. Express cost as a unit metric (per request, per customer) so teams can see efficiency improve even while the total bill grows with the business.
What is the best way for engineering teams to take ownership of cloud cost reduction?
Give each team its own spend data, attributed through accounts and tags, and a target such as a unit cost or a share of revenue, then let them decide how to hit it. The platform team owns shared levers (commitments, cluster consolidation, network endpoints); service teams own their requests, environments, and logging. Showback before chargeback keeps it collaborative.
Should a startup use Savings Plans or Reserved Instances?
For most startups, Compute Savings Plans are the better default because they apply across instance families, Regions, Fargate, and Lambda, so you can keep changing your architecture. EC2 Instance Savings Plans offer a higher maximum discount but lock you to one instance family in one Region. Buy only after cleanup, cover your lowest sustained usage, and stagger purchases.
What should I look for in a cloud cost platform if my team is mostly engineers?
Look for Kubernetes-aware allocation, feedback inside the engineering workflow (pull requests, Slack), and low operational overhead. Check whether it needs agents or ongoing tuning from your platform team. Many teams get most of the value from AWS native tools plus Infracost and OpenCost before a paid platform is justified.
How much can you realistically save?
It depends entirely on your starting point, so be wary of anyone quoting a percentage before looking at your bill. The verifiable ceilings come from AWS itself: up to 66% off On-Demand with Compute Savings Plans, up to 90% with Spot, and up to 70% on instances that only need to run during business hours. The biggest real-world savings usually come from waste you can see in the first week of analysis.
Where to start this week
If you do only three things this week: activate cost allocation tags, turn on Cost Anomaly Detection for every account, and run the three idle-resource queries above. You will have attribution starting to flow, an early warning system, and probably a first round of savings, without taking a single hour from your feature teams. If you want senior engineers to run this playbook with your team, MeteorOps offers a free consultation to review your setup and find the biggest levers.
Top comments (0)