DevOps.Academy
CoursesCurriculumPricing

    Loading…

    ↑ ↓ move · ↵ open · Ctrl K anywhere

    ◔My learningSign in
    DevOps.Academy

    Free, hands-on DevOps lessons. Made with care in India.

    Learn

    All coursesLinux roadmapCurriculumMy learning

    Academy

    PricingCertificates

    Legal

    PrivacyTermsContact

    devops.rajeev.pro

    AWS for DevOpsLesson 17 of 17
    Your progress · 0%
    On this page
    1. The bill is part of the design
    2. Billing and the Free Tier
    3. Budgets and alerts
    4. A zero-spend budget
    5. Cost Explorer
    6. Tags and cost categories
    7. Paying less
    8. The course project
    ↖ Course roadmap
    Lesson 17 · Cost basics

    Cost basics and the course project

    Budgets, billing alerts, Cost Explorer, tags and the ways to pay less, read in a real account. Then the project brief that ties the whole AWS course together.

    30 min read · intermediate · hands-on

    By the end of this lesson you'll be able to

    0 of 6 complete

    The bill is part of the design

    Every lesson in this course ended with a cost line and a clean-up. That habit matters more than any tool here: most surprise AWS bills come from something left running, like a NAT gateway, a load balancer or a database nobody deleted. The tools in this lesson don't stop that. They make sure you find out the same day, and then show you where the money went.

    All of it lives in Billing and Cost Management, a global console (no Region). The screenshots and outputs come from the practice account used for this course. Its account ID is replaced with 111122223333.

    Billing and the Free Tier

    (1) This month's forecast: a projection from usage so far, not a bill. (2) The service costing the most this month, the first place to look.© Screenshot: DevOps Academy

    (1) This month's forecast: a projection from usage so far, not a bill. (2) The service costing the most this month, the first place to look.© Screenshot: DevOps Academy

    The dashboard answers "am I spending, and on what?" in one screen. The currency switch is only for display: AWS bills in US dollars, so learn to read the USD figure. Bills has the itemised invoice for each month.

    The Free Tier page tracks every free offer you're using and how close each one is to its limit:

    (1) Free offers in use this month. (2) How many are at or over their limit: the number to watch. (3) The forecast tells you before you cross a limit, not after.© Screenshot: DevOps Academy

    (1) Free offers in use this month. (2) How many are at or over their limit: the number to watch. (3) The forecast tells you before you cross a limit, not after.© Screenshot: DevOps Academy

    Two kinds of offer show up here. Always free ones (KMS requests, SQS requests, CloudWatch storage) reset every month and never expire. Others depend on when your account was created and change over time, so read the current AWS Free Tier page rather than trusting a blog post. Free Tier covers usage, not mistakes: a NAT gateway or an idle load balancer was never free.

    Budgets and alerts

    A budget compares what you spend with a limit you set, and emails you (or posts to SNS, or runs an action) when a threshold is crossed. Billing → Budgets:

    (1) The budget this account runs with. (2) Thresholds OK: nothing has crossed an alert yet. (3) Create budget, used below.© Screenshot: DevOps Academy

    (1) The budget this account runs with. (2) Thresholds OK: nothing has crossed an alert yet. (3) Create budget, used below.© Screenshot: DevOps Academy

    (1) Actual spend this month: $0.00. (2) The forecast says $8.22, over the limit. (3) The alert email isn't verified yet, so notifications won't reach anyone.© Screenshot: DevOps Academy

    (1) Actual spend this month: $0.00. (2) The forecast says $8.22, over the limit. (3) The alert email isn't verified yet, so notifications won't reach anyone.© Screenshot: DevOps Academy

    This page has two real lessons in it.

    Actual versus forecast. Each alert watches either actual spend (money already spent) or forecasted spend (where AWS thinks the month will end). Here the forecast is already at 164%, but no email went out, because both alerts watch actual spend:

    $ aws budgets describe-notifications-for-budget --account-id 111122223333 --budget-name academy-5usd \
        --query 'Notifications[].[NotificationType,ComparisonOperator,Threshold]' --output text
    ACTUAL	GREATER_THAN	100.0
    ACTUAL	GREATER_THAN	80.0
    
    aws budgets describe-notifications-for-budget --account-id 111122223333 --budget-name academy-5usd \
    • awsthe AWS command line

    A forecast alert warns you mid-month, while there's still time to act; an actual alert confirms it happened. Use both on real budgets. Forecasts early in a month are rough, since they extrapolate from a few days.

    Verify the email. Since September 30, 2026, new email recipients must click a verification link before budget emails reach them. A budget with an unverified address looks healthy and stays silent. After creating one, open the budget and check Email verification says the address is verified.

    A zero-spend budget

    For a learning account, the best budget is one that fires on the first cent. Create budget starts with a choice between a template and full customisation:

    (1) Use a template. (2) Zero spend budget: notifies you once spending exceeds $0.01.© Screenshot: DevOps Academy

    (1) Use a template. (2) Zero spend budget: notifies you once spending exceeds $0.01.© Screenshot: DevOps Academy

    (1) A name. (2) Who gets the email (up to 10 addresses). (3) What it does: one email the moment anything costs money. Create budget is just below this.© Screenshot: DevOps Academy

    (1) A name. (2) Who gets the email (up to 10 addresses). (3) What it does: one email the moment anything costs money. Create budget is just below this.© Screenshot: DevOps Academy

    This walkthrough stopped here and pressed Cancel: the practice account already has its budget. In your own account, press Create budget, then verify the address from the email AWS sends. The Monthly cost budget template is the next step up: a dollar limit with an actual alert and a forecast alert.

    From the CLI, the same check works for any budget:

    $ aws budgets describe-budgets --account-id 111122223333 \
        --query 'Budgets[].{Name:BudgetName,Limit:BudgetLimit.Amount,Actual:CalculatedSpend.ActualSpend.Amount,Forecast:CalculatedSpend.ForecastedSpend.Amount}' --output table
    -------------------------------------------------
    |                DescribeBudgets                |
    +--------+------------+--------+----------------+
    | Actual | Forecast   | Limit  |     Name       |
    +--------+------------+--------+----------------+
    |  0.0   |  8.219     |  5.0   |  academy-5usd  |
    +--------+------------+--------+----------------+
    
    aws budgets describe-budgets --account-id 111122223333 \
    • awsthe AWS command line

    Cost Explorer

    A budget tells you that you spent; Cost Explorer tells you on what. It shows up to 13 months of history (once it's enabled), grouped by service, Region, account, usage type or tag:

    (1) Monthly granularity; switch to Daily to find the day something started. (2) Grouped by service. (3) Six months of this practice account: $0.00, because every lesson cleaned up after itself.© Screenshot: DevOps Academy

    (1) Monthly granularity; switch to Daily to find the day something started. (2) Grouped by service. (3) Six months of this practice account: $0.00, because every lesson cleaned up after itself.© Screenshot: DevOps Academy

    Twenty-seven services used, and the total still rounds to zero. That's the clean-up habit paying off. The usual investigation goes: group by Service to find the expensive one, then by Usage type to see what in it (hours, GB stored, data out), then by Region to find where.

    The same data from the CLI. The Cost Explorer API lives in us-east-1, and every request costs $0.01, so script it sparingly:

    $ aws ce get-cost-and-usage --time-period Start=2026-10-01,End=2026-10-18 --granularity MONTHLY \
        --metrics UnblendedCost --group-by Type=DIMENSION,Key=SERVICE \
        --query 'ResultsByTime[0].Groups[].[Keys[0],Metrics.UnblendedCost.Amount]' --output text \
        | sort -t$'\t' -k2 -gr | head -5
    EC2 - Other	0.0000155226
    Amazon Simple Storage Service	0.0000003192
    Tax	0
    AmazonCloudWatch	0
    Amazon Virtual Private Cloud	0
    
    aws ce get-cost-and-usage --time-period Start=2026-10-01,End=2026-10-18 --granularity MONTHLY \
    • awsthe AWS command line

    EC2 - Other is everything EC2 bills apart from instance hours: EBS volumes and snapshots, NAT gateways, Elastic IPs. It's the line to check first when compute looks cheap but the bill doesn't. Here it's a fraction of a cent.

    Cost Anomaly Detection, in the same menu, learns your normal spend per service and emails you when a day looks unusual. It catches the slow leaks that never cross a budget threshold.

    Tags and cost categories

    The bill knows services and Regions; it doesn't know that the RDS database belongs to the payments team. Tags fix that. Put the same keys on everything (project, env, owner), then activate them as cost allocation tags so they become a dimension in Cost Explorer:

    (1) Every tag key AWS has seen, all Inactive: none of them splits the bill yet. (2) Activate turns a key into a cost dimension.© Screenshot: DevOps Academy

    (1) Every tag key AWS has seen, all Inactive: none of them splits the bill yet. (2) Activate turns a key into a cost dimension.© Screenshot: DevOps Academy

    Two rules catch people out. Tags only count from the moment you activate them (and take up to a day to appear), so activate early. And a tag missing from a resource is a gap in every report, so enforce tags in Terraform with default_tags on the provider instead of hoping people remember.

    $ aws ce list-cost-allocation-tags --status Active --query 'CostAllocationTags[].TagKey'
    []
    
    aws ce list-cost-allocation-tags --status Active --query 'CostAllocationTags[].TagKey'
    • awsthe AWS command line
    • --querypick fields from the JSON response

    Cost categories group spend by your own rules, for example "everything tagged team=payments plus account prod-payments" → category Payments. They're how finance gets a report by team without reading tags. Build them after the tags are in place.

    Paying less

    Once you know where the money goes, there are four levers, in the order to try them:

    LeverWhat it doesFits
    Deleteremove what nobody uses: old snapshots, unattached EBS volumes, idle load balancersalways first, and free
    Right-sizemove to a smaller or newer instance type that still meets the loadservers mostly idle on CPU and RAM
    Savings Planscommit to a dollar amount per hour for 1 or 3 years, pay a lower ratesteady load that will still be here
    Spot (lesson 3)spare capacity at a large discount, taken back with a 2-minute warningCI runners, batch jobs, test fleets

    Compute Optimizer does the right-sizing maths for you: it reads CloudWatch metrics from EC2, Auto Scaling groups, EBS, Lambda and ECS on Fargate and recommends a better fit. It has to be turned on first (opt-in), and this account hasn't opted in:

    $ aws compute-optimizer get-enrollment-status --region ap-south-1 --query status
    "Inactive"
    
    aws compute-optimizer get-enrollment-status --region ap-south-1 --query status
    • awsthe AWS command line
    • --regionwhich Region
    • --querypick fields from the JSON response

    Savings Plans → Recommendations looks at your past on-demand spend and suggests a commitment:

    (1) Compute Savings Plans cover EC2, Fargate and Lambda in any Region. (2) Term, payment and look-back period. (3) No recommendation: this account has no steady usage to commit to.© Screenshot: DevOps Academy

    (1) Compute Savings Plans cover EC2, Fargate and Lambda in any Region. (2) Term, payment and look-back period. (3) No recommendation: this account has no steady usage to commit to.© Screenshot: DevOps Academy

    Zero recommendations is the right answer for a practice account: AWS only recommends one when your average on-demand spend is above $0.10 an hour. A commitment is a contract; buy one for the baseline you're sure of, never for the peak.

    Trusted Advisor runs checks across cost, security, fault tolerance and service limits:

    (1) Nothing flagged. (2) Most checks, including the cost ones, need a Business or higher Support plan.© Screenshot: DevOps Academy

    (1) Nothing flagged. (2) Most checks, including the cost ones, need a Business or higher Support plan.© Screenshot: DevOps Academy

    On the free Basic plan you get the core security checks and service limits. The cost checks (idle load balancers, underused instances, unassociated Elastic IPs) come with a paid Support plan, which a company account usually has. In a personal account, Cost Explorer and the clean-up habit cover the same ground.

    Things that bill while doing nothing

    NAT gateways, load balancers, RDS instances (even stopped ones restart after 7 days), EBS volumes and snapshots, Elastic IPs not attached to a running instance, and public IPv4 addresses. Stopping an EC2 instance stops compute charges only; its volume keeps billing. When you finish a lab, delete, then check the console.

    In production

    Every account gets a monthly budget with forecast and actual alerts going to a team list, not one person. Tags are enforced in Terraform and by a tag policy in AWS Organizations, Cost Anomaly Detection watches every service, and someone owns a monthly cost review. Savings Plans cover the steady baseline; Spot runs CI and batch work.

    The course project

    You've built every piece separately. Now put them together: a production-like AWS environment created from a single terraform apply, and gone again with one terraform destroy. Build it in your own GitHub repository.

    Requirements

    • A VPC across two AZs with public and private subnets (lesson 4)
    • An Application Load Balancer in the public subnets, an Auto Scaling group of at least two instances in the private subnets, reachable only from the load balancer (lesson 5)
    • A static site in a private S3 bucket behind CloudFront with Origin Access Control (lessons 6 and 9)
    • An RDS database in the private subnets, its password in Secrets Manager, never in code (lessons 10 and 15)
    • Instances get permissions from an IAM role; no access keys anywhere (lesson 2)
    • CloudWatch alarms on unhealthy targets and 5xx errors, sent to an SNS topic you subscribe to (lesson 13)
    • default_tags with project, env and owner on the provider, and a monthly AWS Budget with forecast and actual alerts, both defined in Terraform
    • A GitHub Actions workflow that runs terraform fmt -check, validate and plan on pull requests, authenticated with OIDC (lesson 2)

    Suggested repository layout

    aws-project/
    ├── .github/workflows/terraform.yml
    ├── modules/
    │   ├── network/      # VPC, subnets, routes, endpoints
    │   ├── web/          # launch template, ASG, ALB, security groups
    │   ├── data/         # RDS, Secrets Manager secret
    │   └── site/         # S3 bucket, CloudFront, OAC
    ├── main.tf           # wires the modules together
    ├── budget.tf         # aws_budgets_budget with alerts
    ├── variables.tf
    ├── outputs.tf        # ALB DNS name, CloudFront domain
    └── README.md         # what it builds, what it costs, how to destroy it
    

    How to check it works

    1. terraform apply finishes without errors from an empty account.
    2. curl the ALB DNS name a few times: answers come from both AZs.
    3. The CloudFront domain serves your page; the S3 URL itself returns AccessDenied.
    4. Terminate one instance: the Auto Scaling group replaces it and the alarm fires, then clears.
    5. In Cost Explorer the next day, filter by your project tag: the cost shows up there, not as untagged.
    6. terraform destroy, then confirm in the console that no load balancer, database or NAT gateway is left.

    Stretch goals

    • Remote state in S3 with locking, so a second person can run the same code safely.
    • HTTPS on your own domain with Route 53 and an ACM certificate (lessons 8 and 9).
    • Run the app on ECS Fargate instead of EC2 (lesson 11), and compare the cost in Cost Explorer.

    The load balancer, NAT (if you add one) and database bill by the hour. Build, test and destroy in the same session, and keep the zero-spend budget on while you do.

    ⌘
    Mini mission

    A cost check of your account

    Without creating anything, find out how your account is set up for cost: which budgets exist and what they alert on, what this month cost by service, which tags split the bill, and whether right-sizing is turned on. Write down one thing to fix.

    Input: An AWS account and the AWS CLI with read access to BillingOutput: The budget's limit, actual and forecast spend and alert types, the top services this month, the active cost tags and the Compute Optimizer status
    Reveal one safe solution
    $ export AWS_REGION=us-east-1      # the Budgets and Cost Explorer APIs live here
    $ ACC=$(aws sts get-caller-identity --query Account --output text)
    $ aws budgets describe-budgets --account-id $ACC \
        --query 'Budgets[].[BudgetName,BudgetLimit.Amount,CalculatedSpend.ActualSpend.Amount,CalculatedSpend.ForecastedSpend.Amount]' --output text
    academy-5usd	5.0	0.0	8.219
    $ aws budgets describe-notifications-for-budget --account-id $ACC --budget-name academy-5usd \
        --query 'Notifications[].[NotificationType,ComparisonOperator,Threshold]' --output text
    ACTUAL	GREATER_THAN	100.0
    ACTUAL	GREATER_THAN	80.0
    $ aws ce get-cost-and-usage --time-period Start=2026-10-01,End=2026-10-18 --granularity MONTHLY \
        --metrics UnblendedCost --group-by Type=DIMENSION,Key=SERVICE \
        --query 'ResultsByTime[0].Groups[].[Keys[0],Metrics.UnblendedCost.Amount]' --output text \
        | sort -t$'\t' -k2 -gr | head -5
    EC2 - Other	0.0000155226
    Amazon Simple Storage Service	0.0000003192
    Tax	0
    AmazonCloudWatch	0
    Amazon Virtual Private Cloud	0
    $ aws ce list-cost-allocation-tags --status Active --query 'CostAllocationTags[].TagKey'
    []
    $ aws compute-optimizer get-enrollment-status --region ap-south-1 --query status
    "Inactive"
    
    aws budgets describe-budgets --account-id $ACC \
    • awsthe AWS command line
    aws ce list-cost-allocation-tags --status Active --query 'CostAllocationTags[].TagKey'
    • --querypick fields from the JSON response
    aws compute-optimizer get-enrollment-status --region ap-south-1 --query status
    • --regionwhich Region

    The Cost Explorer call costs $0.01; everything else is free, and nothing is created, so there's nothing to clean up. In this account the one thing to fix was clear: the budget only alerts on actual spend and its email isn't verified yet.

    Try it yourself

    0 of 6 steps done

    Run each task in your own account or terminal, then tick it off.

    Knowledge check

    Quick check

    Question 1 of 5

    You created an AWS account for practice. What is the first cost control to set up?

    Go deeper

    Official reading for this lesson

    • DocsManaging your costs with AWS BudgetsAWS Docs ↗
    • DocsAnalyzing your costs with Cost ExplorerAWS Docs ↗
    • DocsUsing cost allocation tagsAWS Docs ↗
    • DocsWhat is AWS Compute Optimizer?AWS Docs ↗
    • DocsWhat are Savings Plans?AWS Docs ↗
    • GuideCost optimization pillar (Well-Architected)AWS Docs ↗

    Or get every quiz answer right and it completes itself.

    ← PreviousAutomation and IaC: CloudFormation, CDK and Systems ManagerUp nextBack to the course roadmap