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:- An EKS cluster with an IAM OIDC provider associated, so the Thoras worker can assume a role through IRSA.
- Cost Explorer enabled on the account that pays for the cluster.
- The
aws:eks:cluster-namecost allocation tag activated in the Billing console of that account.
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
Without Terraform
Create a role with this trust policy, substituting your account ID, OIDC issuer host, and the namespace Thoras is installed in:Enable collection
Point the worker’s service account at the role and turn the collector on: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:
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 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:Cost and usage collector is enabled, then
Collecting daily cost and usage with the date window and the tag Thoras
resolved:
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:
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.
