DEV Community

Cover image for Why Can't You See Per-Environment AWS Costs?
Matt
Matt

Posted on • Originally published at fortem.dev

Why Can't You See Per-Environment AWS Costs?

Why Can't You See Per-Environment AWS Costs?

Originally published at https://fortem.dev/blog/ecs-fargate-cost-visibility
Cost Explorer shows the total. Tags cover Fargate but miss the $90/mo per environment of ALB, NAT, and CloudWatch overhead. Here's why per-env cost is structurally hard on ECS Fargate — and what works.


What Cost Explorer actually shows you

AWS Cost Explorer is a billing tool, not an environment tool. It groups your charges by service, tag, linked account, and region. It does not know what an "environment" is. That concept lives in your head and in your Terraform code — not in the AWS data model.

When you open Cost Explorer and try to answer the per-environment question, here's what you actually get:

  • Total spend by service— "$4,200/mo in Fargate compute, $890 in data transfer, $660 in CloudWatch." Useful, but it's not an environment number.
  • By cost allocation tag— only if you've activated the tag in the Billing console AND tagged every resource. Even then, you get rows for each unique tag value, not grouped environments.
  • By linked account — useful for multi-account setups, but most teams run multiple environments in a single account.

The "staging environment" you have in your head is a logical bundle: 3-15 Fargate services + an ALB + target groups + a NAT Gateway + CloudWatch log groups + an ECR repo + Secrets Manager entries + an S3 bucket or two. Cost Explorer sees 10-20 unrelated line items. None of them say "staging."

The real Fargate pricing breakdown gets you the components. It doesn't get you the per-environment grouping. That's the gap.

Why per-environment cost is hard on ECS Fargate specifically

Per-environment cost is a problem on any AWS service. On ECS Fargate it's particularly bad because an environment is a bundle of services, not a single resource. And the bundle crosses the boundaries of how AWS models tagging.

Here's what tag coverage looks like for a typical 8-service staging environment:

  • Fargate tasks (8 services) — tag follows the ECS service. Works if you tagged the cluster.
  • ALB (1) — usually tagged at cluster level. Sometimes missed.
  • Target groups (8) — tag propagates from the ALB, not always consistent.
  • NAT Gateway (1-2) — lives at the VPC level. No env tag possible.
  • CloudWatch log groups (8-10) — tagged separately, often forgotten.
  • ECR repositories (3-5) — created by ECS, sometimes tagged, sometimes not.
  • Secrets Manager entries (5-10) — often tagged, sometimes managed at account level.

Even with a perfect tagging convention, the NAT Gateway line item is ungroupable. CloudWatch log groups might be. ECR is hit-or-miss. So your "staging" tag covers maybe 60% of the actual cost of running staging.

KEY INSIGHT: An ECS environment is a bundle of services you provision together, not a single resource. AWS pricing is per-resource. The mismatch is the root of the per-environment cost problem.

The $90/mo/env blind spot: shared overhead tags can't see

Here's the number that should be in every platform engineer's head: every ECS environment carries ~$90/mo of fixed overhead that is never captured in a tag-based report. The full breakdown is in our real Fargate pricing post; the short version:

  • Application Load Balancer: ~$22/mo base + LCU charges. Required for any HTTP service that needs a stable URL.
  • NAT Gateway: ~$33/mo per AZ (~$66/mo at 2 AZs for HA). Charges for outbound traffic from private subnets.
  • CloudWatch logs: $0.50/GB ingest + $0.03/GB-mo storage. Most staging envs process 20-50 GB/mo.
  • Secrets Manager + ECR + SSM:~$5-10/mo combined per env. Usually small, but tags don't always propagate.

The $90 is real and unavoidable.It bills whether your tasks are running or stopped. It bills on Saturday at 3am when nobody's looking. For 20 environments, that's $1,800/mo — $21,600/yr — sitting in your bill, untagged, unattributable to any single environment.

This is the number that the CFO question is really asking about. When she says "what does staging cost," she's not asking about Fargate tasks. She's asking about the full bundle. The tag-based approach gives her an answer that's missing 30-40% of the real number.

Why cost allocation tags fail in practice

Cost allocation tags are AWS's official answer to the per-resource attribution problem. They work in theory. In practice, they fail in five predictable ways.

1. Tags must be activated separately

The Environment tag on your ECS services does not show up in Cost Explorer until you activate it in the Billing console. This is a separate, one-time-per-tag step. Most teams forget. Even AWS's own documentation notes it takes up to 24 hours for tags to appear after activation.

2. Tags only work from the moment of activation

No backfill. If you activated your Environment tag on June 1, your May cost data has no environment breakdown. AWS does not compute historical attribution from newly-activated tags.

3. Inconsistencies split data silently

Env=Prod and env=prodare two different tag values in AWS's view. Environment=staging and Environment=staging-1 are two different tag values. A team that evolves its naming convention ends up with three rows of "staging" in Cost Explorer, none of which are the full picture.

4. Shared services can't be tagged per-env

A NAT Gateway is a property of a VPC, not of an environment. A CloudWatch Logs group is per-service, not per-env. ECR repos are often shared across envs. These costs are real and per-env-influenced, but no tag will ever attribute them to a specific environment.

5. Multi-account setups compound the problem

If staging runs in account A and prod runs in account B, you can't aggregate them in a single Cost Explorer view without enabling Cross-Account Cost Allocation. Even when enabled, you get a per-account view — not a per-environment view if both accounts host multiple envs.

As CloudZero's own tagging analysis puts it: "Tagging fails in shared or multi-tenant environments." The fact that CloudZero (a competitor to Fortem in some ways) writes this so plainly is telling.

What actually works: a hybrid model

The answer is not "tag harder." It's stop trying to make one mechanism solve a multi-mechanism problem. Here's the hybrid model that actually produces a usable per-environment number.

Layer 1: Tags for Fargate compute (the easy 60%)

Cost allocation tags work fine for Fargate tasks. Activate the tag, apply it consistently, and you get per-env Fargate cost. This is roughly 60% of the total per-env cost on a typical fleet.

Layer 2: A fixed overhead model for shared services (the missing 30%)

For the $90/mo/env of NAT + ALB + CW that tags can't see, allocate by a simple model: each environment gets $X fixed overhead + $Y per Fargate service running in it. The exact numbers depend on your architecture (one shared ALB vs many; one VPC NAT vs per-env NAT), but the model is straightforward and doesn't require any AWS-side changes.

Layer 3: Read-time calculation for real-time answers (the remaining 10%)

Cost Explorer data lags 24 hours. For some questions — "what did that scheduling change save us this week" — you need real-time. A read-time calculation reads your ECS task definitions, sums vCPU × $0.04048 and GB × $0.004445, and multiplies by hours running. It's how Fortem's AI skill computes the per-environment number on the fly. You can also do it with a 10-line bash script — see the next section.

None of the three layers is sufficient alone. Together, they give you a per-environment cost number that holds up to the CFO question.

The DIY version (10-line script)

If you have fewer than 10 environments and you just need a one-time per-env cost number, this bash script does the Fargate-only calculation. It reads every ECS task in every cluster, looks up vCPU and memory from the task definition, multiplies by Fargate pricing, and prints a table.

It does not include ALB, NAT, or CloudWatch — those are the "blind spot" already covered above. Add your $90/env fixed overhead to the result for a rough total.

#!/usr/bin/env bash
# Requires: aws cli, jq, bc. Pricing: us-east-1 Linux/x86 on-demand (May 2026).

set -euo pipefail
VCPU_RATE=0.04048   # $/vCPU-hour
MEM_RATE=0.004445   # $/GB-hour
HOURS=730           # hours per month

for cluster in $(aws ecs list-clusters --query 'clusterArns[]' --output text); do
  name=$(basename "$cluster")
  cost=0
  for task in $(aws ecs list-tasks --cluster "$cluster" --query 'taskArns[]' --output text); do
    td=$(aws ecs describe-tasks --cluster "$cluster" --tasks "$task" \
      --query 'tasks[0].taskDefinitionArn' --output text)
    cpu_mem=$(aws ecs describe-task-definition --task-definition "$td" \
      --query 'taskDefinition.{cpu:cpu,memory:memory}' --output text)
    cpu_units=$(echo "$cpu_mem" | awk '{print $1}')
    mem_mib=$(echo "$cpu_mem" | awk '{print $2}')
    vcpu=$(echo "scale=4; $cpu_units / 1024" | bc)
    gb=$(echo "scale=4; $mem_mib / 1024" | bc)
    rate=$(echo "scale=4; $vcpu * $VCPU_RATE + $gb * $MEM_RATE" | bc)
    cost=$(echo "scale=2; $cost + $rate * $HOURS" | bc)
  done
  printf "%-40s $%s/mo\n" "$name" "$cost"
done
Enter fullscreen mode Exit fullscreen mode

Run it. Get a table. Add $90/env for the overhead you can't see. That's your per-environment number. It works for fleets up to ~10 envs.

When to graduate from DIY

The bash script is honest and useful for small fleets. It starts to hurt when:

  • You have 15+ environments and the script takes 20+ minutes to run
  • You need real-time cost (the script is a snapshot, not continuous)
  • You have Fargate Spot in some envs and on-demand in others (Spot needs separate calculation)
  • You need to track cost over time, not just right now
  • Multi-account fleets need aggregation

At that point, you need a system that runs continuously, not a script. Fortem is one option (it does the same calculation as the script, but continuously, with Fargate Spot handled, with multi-account support, and with a real UI). Vantage and CloudZero are others (different approaches — they focus on total AWS spend and add tags, where Fortem starts from the ECS environment and computes what tags can't see).

Whichever you pick, the question you should be asking is: _does it answer the CFO question in 5 seconds, or do I have to export a CSV and explain the methodology first?_If the latter, the tool isn't doing the job.

Questions you probably have next

Not product FAQ. The things you actually wonder about after reading.

Does AWS Cost Categories solve the per-environment problem?

Partially. Cost Categories let you group resources by tag combinations and account structure into named buckets (like "Production" or "All-Staging"). They work on top of tags — so they don't fix the untaggable shared services. Useful, but not a full replacement for the hybrid model.

Can I backfill tags to see historical per-environment cost?

No. AWS does not backfill cost allocation tags. If you activated your Environment tag on June 1, your May bill has no per-env breakdown — even if every resource had the tag. The data is gone. Activate early, before you actually need the answer.

How do I split NAT Gateway cost across environments that share a VPC?

You can't, not perfectly. Best approximations: (1) by GB data processed per env, if you have VPC Flow Logs and can attribute traffic; (2) evenly, by number of envs sharing the VPC. Neither is exact, but both are better than ignoring the cost. The fixed-overhead model (Layer 2 above) sidesteps this by treating shared services as a single per-env allocation, not as something to split.

What's the difference between Cost Categories and Cost Allocation Tags?

Cost allocation tags are resource-level labels you apply to individual resources. Cost Categories are higher-level groupings that combine tag values and account structure into named buckets. Tags are the raw input; Categories are derived views. Both depend on the underlying tag discipline.

Common questions

Specific to Fortem and the DIY approach.

Does Fortem replace AWS Cost Explorer?

No. Fortem reads your AWS account and computes per-environment cost in real time from Fargate pricing, ECS task definitions, and running task counts. It doesn't ingest your Cost & Usage Reports. Cost Explorer remains the source of truth for total AWS spend and historical billing data. Fortem answers the question Cost Explorer can't: what does each environment cost right now, and how does that change when you turn environments on and off?

How long does it take to set up per-environment cost reporting?

Reading your fleet and computing cost from Fargate pricing takes about 5 minutes via Fortem's AI skill (download .md, run in your AI agent, get the report). Manual DIY with the bash script in this article takes 30 minutes if you have AWS CLI configured. Full Fortem onboarding with continuous cost tracking is 7 business days — but the first fleet report is instant.

Can I export per-environment cost to a spreadsheet?

Yes. Fortem's discovery report is a self-contained HTML file you can open in any browser — copy the table into Sheets or Excel. The YAML file is also importable. Cost Explorer data is exportable to CSV. The bash script in this article outputs to the terminal, but piping to column -t -s $'\t' gives a paste-ready table.

Does Fortem work without any cost allocation tags?

Yes. Fortem reads cluster names, service names, and task definitions — not tags. It parses naming conventions like use1-prod-main and staging-cluster-1 to infer stages. Tagging helps with edge cases (orphaned resources, unusual names), but a clean naming convention is enough for most fleets.


Map your fleet in 5 min: fortem.dev/audit

Top comments (0)