All blogs
    AWSOptimizationDevOps

    AWS Cost Optimization for Startups: A Practical Playbook

    August 11, 202610 min read
    AWS Cost Optimization for Startups: A Practical Playbook

    Every startup's AWS bill has the same story arc. Month one: $40, mostly free tier. Month eight: $600, still fine. Month fourteen: someone opens the invoice, sees $9,400, and asks the question nobody can answer — what is actually running in there?

    The bill didn't explode because of one bad decision. It grew because a hundred small ones never got revisited: the staging cluster nobody turned off, the RDS instance sized for a launch that went fine, the NAT Gateway quietly charging for every byte your containers pull from the internet.

    This is a playbook for fixing that without hiring a FinOps team or freezing engineering for a sprint.


    Step 1: You can't cut what you can't see#

    Before touching a single instance, get visibility. Optimizing blind is how teams spend two weeks saving $60/month while a $2,000/month line item sits untouched.

    Do these three things this week:

    1. Turn on Cost Explorer and group by service. Look at the last 90 days. In almost every startup account, 70–80% of the bill comes from three or four services. Those are the only ones worth your attention right now.
    2. Set up cost allocation tags. At minimum: Environment (prod/staging/dev), Team, and Service. Activate them in the Billing console — tags created but not activated won't show up in reports. Untagged resources are where waste hides.
    3. Create AWS Budgets with alerts. One monthly budget at your expected spend, plus alerts at 80% and 100%. Also enable Cost Anomaly Detection — it's free and it catches the "someone spun up a GPU instance for a test and forgot" scenario within a day instead of at month close.

    If your spend is above roughly $10k/month, also enable the Cost and Usage Report (CUR) into S3 and query it with Athena. Cost Explorer rounds off the detail you need at that scale.


    Step 2: The first-week cleanup (fastest money you'll ever save)#

    This is pure waste removal. No architecture changes, no risk to production, no tradeoffs. Most startups find 10–25% of their bill here.

    Hunt for these:

    • Unattached EBS volumes. Instances get terminated, volumes survive. You're paying for empty disks.
    • Old EBS snapshots. Snapshots from 2023 that nobody will ever restore. Set a lifecycle policy going forward.
    • Idle load balancers. An ALB with no healthy targets still costs ~$16–20/month plus LCU charges.
    • Unassociated Elastic IPs. AWS charges for IPv4 addresses that aren't attached to anything — and, as of 2024, for public IPv4 addresses generally.
    • Forgotten environments. That "temporary" load-test cluster, the demo environment for a customer who churned, the second staging setup from the migration.
    • CloudWatch Logs with infinite retention. Log groups default to never expire. Set retention to 7–30 days for dev and 90 days for prod unless compliance says otherwise. This one surprises people — log storage can quietly become a four-figure line item.
    • Old Elastic Container Registry images. Add a lifecycle rule to keep the last N tagged images and expire untagged ones.

    Then schedule your non-production resources. Dev and staging typically run 24/7 for a team that works 40 hours a week. Shutting them down nights and weekends cuts those environments by roughly 65–70%. AWS Instance Scheduler does this, or a scheduled Lambda if you prefer something you control. For Aurora and RDS, use start/stop schedules; for EKS, scale node groups to zero.


    Step 3: Right-size what's left#

    Now look at whether the things you do need are the size you actually need.

    Open AWS Compute Optimizer — it's free and it analyzes CloudWatch metrics to flag over-provisioned EC2 instances, EBS volumes, Lambda functions, and ECS services. Treat its recommendations as a strong starting point, not gospel; it can't see your traffic spikes or batch jobs if they fall outside the lookback window.

    The pattern to look for: instances sitting at 5–15% CPU with memory to spare. Someone picked m5.2xlarge because it felt safe, and it's been coasting ever since.

    Databases are usually the biggest single win. RDS and Aurora instances get sized generously at launch and never revisited. Check CPUUtilization, FreeableMemory, and connection counts. Dropping one production RDS instance from db.r6g.2xlarge to db.r6g.xlarge is often several hundred dollars a month for zero user-visible change.

    Right-size before you commit to discounts. Buying a three-year commitment on an over-provisioned fleet locks in your waste.


    Step 4: Commitment discounts (Savings Plans, RIs, Spot)#

    Once your baseline is stable and correctly sized, buy discounts on it. This is where the biggest percentage savings live.

    OptionTypical discountCommitmentBest for
    Compute Savings Plansup to ~66%1 or 3 years, $/hourSteady baseline compute; flexible across EC2, Fargate, Lambda, instance families, and regions
    EC2 Instance Savings Plansup to ~72%1 or 3 yearsSteady workloads where you're confident in the instance family and region
    Reserved Instancesup to ~72%1 or 3 yearsRDS, ElastiCache, OpenSearch, Redshift — these still use RIs, not Savings Plans
    Spot Instancesup to ~90%noneBatch jobs, CI runners, stateless workers, ML training — anything that tolerates a 2-minute interruption notice

    For a startup, the practical advice is:

    • Start with 1-year, no-upfront Compute Savings Plans. Three-year all-upfront saves more, but you probably can't predict your architecture 36 months out, and an unused commitment is worse than no commitment.
    • Only cover 60–80% of your baseline, not 100%. Leave headroom so a migration or an efficiency win doesn't strand you paying for capacity you no longer use.
    • Don't forget RDS Reserved Instances. Teams buy Savings Plans, feel done, and leave a large database bill entirely on-demand.
    • Move CI, batch, and async workers to Spot. This is often the single highest-leverage architectural change. Karpenter on EKS handles Spot interruption gracefully and will mix Spot and on-demand automatically.

    Step 5: Cheaper hardware for the same work#

    Move to Graviton. AWS's ARM-based instances (the *g families — m7g, c7g, r7g, db.r7g) run roughly 20% cheaper than the equivalent x86 instances, often with better performance per dollar. If you're on Python, Node, Go, Java, or Ruby in containers, the migration is frequently a one-line change to your base image plus a multi-arch build. RDS, ElastiCache, and OpenSearch all offer Graviton instance types too, and switching those is usually just a modify-instance operation during a maintenance window.

    Switch EBS gp2 volumes to gp3. gp3 is about 20% cheaper per GB and lets you provision IOPS independently of size. There is no downside and no downtime — it's a volume modification. If you still have gp2 volumes, this is free money.


    Step 6: Storage and data transfer (the silent killers)#

    S3: Most startups store everything in S3 Standard forever. Enable S3 Intelligent-Tiering on buckets with unpredictable access patterns — it moves objects between tiers automatically for a small monitoring fee. For known patterns (logs, backups, old user uploads), write explicit lifecycle rules: Standard → Standard-IA at 30 days → Glacier Instant Retrieval or Deep Archive at 90–180 days. Also enable a lifecycle rule to abort incomplete multipart uploads after 7 days; failed uploads leave orphaned parts you're paying to store and can't see in the console.

    Data transfer is where bills go strange, because it's the one cost that doesn't map to anything you provisioned:

    • NAT Gateway charges an hourly rate plus a per-GB processing fee. If your containers pull images or hit S3 through a NAT Gateway, you're paying for traffic that could be free. Add VPC Gateway Endpoints for S3 and DynamoDB — they cost nothing and remove that traffic from NAT entirely. This alone has cut four-figure monthly line items for plenty of teams.
    • Cross-AZ traffic costs money in both directions. A chatty microservice architecture spread across three AZs pays a tax on every internal call. Keep tightly-coupled services in the same AZ where your availability requirements allow it.
    • Internet egress is expensive at scale. Put CloudFront in front of anything serving significant traffic to users — CloudFront's egress rates are lower than direct EC2/S3 egress, and origin-to-CloudFront transfer is free.

    Step 7: Take the free money#

    If you haven't already, apply to AWS Activate. Startups can receive meaningful credits (tiers range from a few thousand dollars up to $100k for companies backed by a partnered VC or accelerator), plus technical support. If you're in an accelerator or have an institutional investor, you likely qualify for a larger tier than the self-serve one — ask your investor for the org ID.

    Credits are a runway extender, not a strategy. Teams that live on credits for eighteen months and never look at the bill get a brutal surprise the month they expire. Optimize as if you were paying full price.


    Making it stick#

    The cleanup is the easy part. Preventing the regrowth is what actually matters.

    • Review the bill monthly. Fifteen minutes, one person, Cost Explorer grouped by service and by tag. Look for anything that grew more than your usage did.
    • Make cost visible per team. Once engineers can see what their service costs, they optimize it without being asked. Untagged spend should be a number you're actively driving toward zero.
    • Enforce tagging at creation. Terraform default tags, or an SCP that blocks untagged resource creation. Retroactive tagging is miserable.
    • Add a cost line to design docs. "What will this cost at 10x current traffic?" catches expensive architecture before it ships, which is a hundred times cheaper than catching it after.

    A realistic 30-day plan#

    Week 1 — Enable Cost Explorer, Budgets, and Anomaly Detection. Set up tags. Identify your top four services.

    Week 2 — Delete unattached volumes, old snapshots, idle load balancers, unused IPs. Set CloudWatch Logs retention. Schedule dev/staging shutdowns.

    Week 3 — Run Compute Optimizer. Right-size EC2 and RDS. Convert gp2 → gp3. Add S3 lifecycle rules and VPC endpoints for S3/DynamoDB.

    Week 4 — Buy 1-year no-upfront Savings Plans covering ~70% of your stabilized baseline. Buy RDS Reserved Instances. Plan your Graviton and Spot migrations for next quarter.

    A startup doing all of this typically lands somewhere between 30% and 50% below where it started — without degrading a single user-facing thing. The point isn't to be frugal for its own sake. It's that every dollar not going to idle infrastructure is a dollar of runway going toward the thing you're actually building.


    Pricing figures and discount ranges vary by region, instance family, and current AWS pricing. Verify against the AWS Pricing Calculator and your own Cost Explorer data before making commitments.

    Thanks for reading!
    More bite-sized engineering notes are waiting on the blog.

    © 2025 Arpan Pokharel