Use Case

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

Your CTO asks what your staging environment costs per month. You open Cost Explorer. You find the answer is either the entire AWS bill or a per-service breakdown that doesn't map to environments. Per-environment cost is structurally hard on ECS Fargate — this post explains why, and what works.

Matt S
Matt S
Platform engineer at Fortem · June 5, 2026 · 8 min read
TL;DR
  • An ECS Fargate environment is a bundle of heterogeneous services — Fargate tasks, ALB, NAT, CloudWatch logs, ECR, Secrets Manager. Tagging covers the Fargate part. It misses the rest.
  • The untaggable overhead is roughly $90/mo per environment (ALB + NAT + CloudWatch). For a team with 20 envs, that's $1,800/mo invisible in any tag-based report.
  • Env=Prod and env=prod create separate line items. Casing, plural vs singular, and missing tags silently split your cost data.
  • Cost Explorer data lags ~24 hours for the current month. You can't answer "what did staging cost this morning" — only what it cost yesterday.
  • A read-time calculation (Fargate pricing × actual vCPU/GB) bypasses tagging entirely. There's a 10-line bash version below.

What Cost Explorer shows you

Cost Explorer shows total AWS spend by service — not by environment. Tags get you close, but untaggable shared resources (ALB, NAT) always fall outside any per-environment view.

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, the results fall into three categories:

  • 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.

Download the skill file — let AI compute it

A downloadable AI skill reads your ECS clusters, prices each task from Fargate rates, and adds shared-overhead estimates — no tags required, runs locally against your AWS account.

Per-Environment Cost Calculator
Lists every ECS cluster, computes Fargate compute cost from task definitions, adds the ALB/NAT/CloudWatch overhead, and prints a per-environment table with a fleet total — read-only, no tags required.
Read-only by default· Runs locally· No tags required
Drop into Claude Code, OpenCode, or Codex — the agent executes the steps

Why per-environment cost is hard on ECS Fargate specifically

ECS Fargate environments span Fargate tasks, ALB, NAT Gateway, and CloudWatch — only tasks are taggable. Untaggable shared services account for roughly 40% of total environment cost.

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.

Tag coverage for a typical 8-service staging environment breaks down like this:

  • 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

Every ECS environment carries roughly $90/month in fixed overhead from ALB, NAT Gateway, and CloudWatch that tags cannot capture — 30–40% of true environment cost is invisible to Cost Explorer.

“These costs are flat — they don't go away when you stop tasks at night, and they don't appear on the compute line in Cost Explorer.”

— Verified on live ECS production deployment, June 2026

Every platform engineer should have this number in their 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

Tags fail in five ways: manual activation, limited backfill, silent casing splits, untaggable shared VPC services, and multi-account gaps that cross-account cost allocation does not fully solve.

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 — with one exception

If you activated your Environment tag on June 1, your May cost data has no environment breakdown by default. AWS does not automatically compute historical attribution from newly-activated tags. However, management account users can request a tag backfill for up to 12 months — but only for months where the tag was already assigned to the resource. If the tag didn't exist on the resource in May, backfill won't help. Activate early and assign tags at resource creation time.

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.

The hybrid model that works

Tag every taggable resource (Fargate tasks, ECS services, RDS), then allocate untaggable shared services proportionally by environment count. Together, these two layers reach 90% accuracy.

The answer is not "tag harder." It's stop trying to make one mechanism solve a multi-mechanism problem. The hybrid model below 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)

A ten-line bash script multiplies vCPU and GB counts by Fargate on-demand rates to print per-cluster Fargate cost. Add $90 per environment for ALB, NAT, and CloudWatch to get the full number.

If you have fewer than 10 environments and 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.

bash
#!/usr/bin/env bash
# per-env-cost.sh — compute Fargate cost per ECS cluster (proxy for env)
# 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

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 breaks at 15+ environments, Fargate Spot mixed with on-demand, or multi-account fleets — past those thresholds, continuous per-environment calculation replaces one-off snapshots.

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 only a point-in-time snapshot
  • 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

After reading, teams ask: does the bash script work with Fargate Spot, can tags capture shared ALB costs, and at what fleet size does DIY cost tracking become more expensive than the tooling.

Not product FAQ. The things you tend to 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?

Partially. Management account users can request a backfill for up to 12 months — but only for months where the tag was already assigned to the resource. If the tag didn't exist on that resource in May, no backfill will recover it. Activate early and assign tags at resource creation.

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.

The numbers in this post are estimates. Run the Fleet Audit against your actual ECS fleet and get your real figure in 15 minutes.

If you read this, you might also want to know

How do I split shared ALB costs across environments?

Attribute proportionally by request count. If your shared ALB serves 3 environments and dev gets 40% of requests, dev pays 40% of the ALB cost. Use ALB access logs to get per-target-group request counts. This gets roughly 90% accuracy.

Does AWS have a native per-environment cost view?

Not natively. ECS Split Cost Allocation Data (2023) attributes Fargate spend per cluster and service — but only if your naming is consistent. It doesn't attribute ALB, NAT Gateway, or CloudWatch costs. For a complete per-environment view, you need a tool or manual model.

Should I use separate AWS accounts per environment for better cost visibility?

Separate accounts give you clean billing boundaries but add management overhead (IAM, VPC peering). Best middle ground: one account for production (clean cost tracking) and one shared non-prod account (lower overhead for environments that don't need strict isolation).

See what each of your environments costs

Run the free AI audit. It reads your AWS, lists every ECS environment, and gives you the per-environment cost in 5 minutes. No Terraform changes needed.

Response within 4 hours, weekdays.