Skip to main content
Thoras estimates cluster cost from a built-in static price list. Those estimates are directional by design — they don’t know about your reserved instances, savings plans, or negotiated rates. See Cost Estimates for how that calculation works. Enabling AWS Cost Explorer collection gives Thoras the spend AWS actually billed you, broken down by service and usage type for each day. Cost reporting uses this in place of the estimates, so the figures you see match your invoice. Collection is off by default. AWS bills every Cost Explorer API request, so turning it on is an explicit opt-in.
Cost Explorer collection reads AWS billing, so it applies to EKS clusters. Thoras skips collection on clusters it detects are running in another cloud rather than paying for a query that cannot return anything.

Before you start

You need:
Activating the cost allocation tag is the step most often missed. Thoras filters your account’s spend down to this cluster using that tag, and Cost Explorer only returns tagged spend for tags that have been activated. If the tag is inactive, every query succeeds and returns nothing. Activation can take up to 24 hours to take effect, and tag data may not reach all the way back through the initial 90-day backfill window.

Grant access to Cost Explorer

The Thoras worker reaches Cost Explorer with whatever AWS credentials its pod picks up. On EKS, bind an IAM role to the worker’s service account with IRSA. The role needs a single permission, ce:GetCostAndUsage. Cost Explorer does not support resource-level permissions, so the policy is scoped to *. Thoras also resolves your AWS account ID through sts:GetCallerIdentity, which requires no permission of its own.

Terraform

Activate the cost allocation tag in the same run. This resource belongs in the account that pays the bill, which may be a different provider alias than the one holding the cluster:

Without Terraform

Create a role with this trust policy, substituting your account ID, OIDC issuer host, and the namespace Thoras is installed in:
Attach this permission policy to it:

Enable collection

Point the worker’s service account at the role and turn the collector on:
Then helm upgrade your release. The worker restarts and begins collecting immediately.

How Thoras identifies your cluster’s spend

Your AWS account bills for more than one cluster, so Thoras narrows each query to a cost allocation tag. It reads that tag from your node labels: If none of your nodes carry those labels — self-managed nodes often don’t — collection stops and logs the labels it looked for. Set the tag yourself in that case:
The value is a TAG_KEY=TAG_VALUE pair and replaces the node label lookup entirely. Use it when your spend is tracked under your own tagging scheme rather than the AWS-generated cluster tag. Whatever key you choose must be activated as a cost allocation tag, the same as aws:eks:cluster-name.

What Thoras collects

Each run stores one row per day, per AWS service, per usage type:
  • Billed cost — the cost as it appears on your invoice.
  • Effective cost — amortized and commitment-adjusted, so reserved instance and savings plan fees are spread across the term they cover rather than landing on the day they were charged.
  • Usage quantity and unit — how much of that usage type was consumed.
The collector runs every 24 hours. AWS finalizes a day’s costs well after the day ends, so collecting more often would not make the data fresher.
  • The first run backfills 90 days.
  • Every run after that re-fetches the last 7 days, overwriting what it already stored. AWS restates recent days as usage finalizes, and this picks up those corrections.
  • The job also runs when the worker starts, but skips if the stored data is less than 20 hours old, so a rollout or a crash loop doesn’t pay for the same days again.

What this costs

AWS charges per Cost Explorer API request — $0.01 per paginated request as of September 15, 2026. See AWS Cost Management pricing for current rates. A nightly run costs a handful of requests. The 90-day backfill on first run is larger but one-time. Cost scales with how many distinct service and usage type combinations your account bills for, since that determines how many pages each query returns.

Verify it’s working

Check the worker logs:
On startup you should see Cost and usage collector is enabled, then Collecting daily cost and usage with the date window and the tag Thoras resolved:
Confirm tagValue matches your cluster. If it doesn’t, set costExplorerFilter explicitly.

Troubleshooting

Cost and usage collection returned no rows — the query succeeded but matched no billed resources. Almost always the cost allocation tag isn’t activated, or the tagKey and tagValue in the log don’t match how your resources are actually tagged. Check the Billing console first, then verify the tag against a node in the EC2 console. no cost filter could be resolved — none of your nodes carry a recognized cluster name label. The log lists the labels Thoras searched for. Set thorasWorker.costExplorerFilter to your own TAG_KEY=TAG_VALUE pair. AccessDeniedException when calling Cost Explorer — the worker assumed a role without ce:GetCostAndUsage, or didn’t assume one at all. Confirm the service account carries the annotation:
If the annotation is there but the role isn’t being assumed, the trust policy’s sub condition likely doesn’t match — it must name the namespace Thoras is installed in and the worker’s service account name. Could not build the AWS Cost Explorer provider — the worker couldn’t read its AWS configuration. It keeps running and collects nothing rather than failing startup. Check for a malformed AWS config file or environment. The cost and usage provider does not bill the cloud this cluster runs in — Thoras detected the cluster is running somewhere other than AWS and skipped the query. Setting costExplorerFilter bypasses this check if you’re intentionally attributing non-EKS spend.