Skip to main content

Installing on Amazon Web Services (AWS)

OpenCost may be installed on Kubernetes clusters running on EC2 or on Elastic Kubernetes Service (EKS).

You will need to install Prometheus, create your OpenCost namespace, configure your cluster pricing and cloud costs, and then install OpenCost.

Install Prometheus​

Prometheus is a prerequisite for OpenCost installation. OpenCost requires Prometheus for scraping metrics and data storage. For the installation of Prometheus please use the following command:

helm install prometheus --repo https://prometheus-community.github.io/helm-charts prometheus \
--namespace prometheus-system --create-namespace \
--set prometheus-pushgateway.enabled=false \
--set alertmanager.enabled=false \
-f https://raw.githubusercontent.com/opencost/opencost/develop/kubernetes/prometheus/extraScrapeConfigs.yaml

This will install Prometheus in the prometheus-system namespace with default settings for use with OpenCost.

If you wish to use a different Prometheus installation or work with another compatible technology, please refer to the Prometheus installation page.

Create the OpenCost Namespace​

Create the opencost namespace for your installation:

kubectl create namespace opencost

Alternate namespaces may be used if necessary.

AWS Configuration​

Cost Allocation​

OpenCost will automatically read the node information node.spec.providerID to determine the cloud service provider (CSP) in use. If it detects the CSP is AWS, it will attempt to pull the AWS on-demand pricing from the configured public API URL with no further configuration required.

OpenCost will request pricing data from the us-east-1 region for your node_region using the template:

https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonEC2/current/${node_region}/index.json

The location should be automatically detected, but this URL can be overwritten using the environment variable AWS_PRICING_URL.

AWS Spot Instance Data Feed​

AWS Spot Instance Data Feed from the configured S3 bucket

  • Set up Spot Instance Data Feed.
  • Create a role for OpenCost with access to the spot feed bucket. Attach the policy below to the role and replace CHANGE-ME with your spot bucket name.
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"s3:ListAllMyBuckets",
"s3:ListBucket",
"s3:HeadBucket",
"s3:HeadObject",
"s3:List*",
"s3:Get*"
],
"Resource": [
"arn:aws:s3:::CHANGE-ME"
],
"Effect": "Allow",
"Sid": "SpotDataAccess"
}
]
}

Configuration​

The data feed will provide specific pricing information about any Spot instances in your account on an hourly basis. After setting this up, the bucket information can be provided through options in the AWS provider configuration file.

  • awsSpotDataBucket - The name of the S3 bucket Spot Instance Data Feed is publishing to.
  • awsSpotDataRegion - The region configured for Spot Instance Data Feed
  • awsSpotDataPrefix - The prefix (if any) configured for Spot Instance Data Feed
  • projectID - The AWS Account ID

Example configuration:

{
"provider": "aws",
"description": "AWS Provider Configuration. Provides default values used if instance type or spot information is not found.",
"CPU": "0.031611",
"spotCPU": "0.006655",
"RAM": "0.004237",
"GPU": "0.95",
"spotRAM": "0.000892",
"storage": "0.000138888889",
"zoneNetworkEgress": "0.01",
"regionNetworkEgress": "0.01",
"internetNetworkEgress": "0.143",
"spotLabel": "kops.k8s.io/instancegroup",
"spotLabelValue": "spotinstance-nodes",
"awsSpotDataRegion":"us-east-2",
"awsSpotDataBucket": "my-spot-bucket",
"awsSpotDataPrefix": "spot_pricing_prefix",
"projectID": "012345678901"
}

Security for AWS integration​

OpenCost uses the AWS SDK for Go to pull Spot data feed information. There are multiple supported ways to configure security.

The recommended setup is to leverage IAM roles for Service Accounts or EKS Pod Identities.

IAM Roles for Service Accounts​

After creating the role and policy, attach the role as an annotation on the service account in your local OpenCost Helm values file:

serviceAccount:
create: true
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/S3Access

EKS Pod Identities​

See AWS Documentation on EKS Pod Identities

To use EKS Pod Identities with OpenCost:

  1. Configure the EKS Pod Identities add on: https://docs.aws.amazon.com/eks/latest/userguide/pod-id-agent-setup.html
  2. Create the EKS Pod Identity association: https://docs.aws.amazon.com/eks/latest/userguide/pod-id-association.html
  3. Update your local OpenCost Helm values file with:
serviceAccount:
create: true
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/S3Access
name: name-of-service-account-from-step-2 # can be omitted if the name of the service account is exactly 'opencost'

AWS Cloud Costs​

info

The Cloud Costs feature is included in the stable releases as of 1.108.0. Please ensure you have the latest release to access this new feature.

Cloud Costs reads your AWS Cost and Usage Report (CUR) through Amazon Athena. You need:

  1. A CUR delivered to S3, either:
    • CUR 2.0 (recommended), from Billing and Cost Management > Data Exports, or
    • CUR 1.0 (legacy), from Billing and Cost Management > Cost and Usage Reports.
  2. An Athena database and table over that bucket. Both export types can create these for you.
  3. An S3 bucket for Athena query results, named aws-athena-query-results-*.

CUR 2.0​

OpenCost detects the version automatically: a table with any of resource_tags, line_item_usage_account_name, or bill_payer_account_name is treated as CUR 2.0. For CUR 2.0, OpenCost reads tags from the resource_tags map (resource_tags['user_team']) instead of per-tag columns, and filters on the billing_period partition instead of year/month.

When defining a CUR 2.0 export, include resource_tags and resource IDs. Without resource_tags, OpenCost falls back to CUR 1.0 handling and attributes no spend to Kubernetes beyond AmazonEKS line items.

Configuration file​

Cloud Costs are configured with a cloud-integration.json file containing one or more Athena integrations:

{
"aws": {
"athena": [
{
"bucket": "s3://aws-athena-query-results-my-cur",
"region": "us-east-1",
"database": "athenacurcfn_my_cur",
"table": "my_cur_2_0",
"workgroup": "primary",
"account": "123456789012",
"authorizer": {
"authorizerType": "AWSAccessKey",
"id": "AKIAIOSFODNN7EXAMPLE",
"secret": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
}
}
]
}
}
FieldRequiredDescription
bucketYess3:// URI of the Athena query results bucket. OpenCost reads and writes it.
regionYesAWS region Athena runs in.
databaseYesAthena database. For a CloudFormation-created legacy CUR, the physical ID of AWSCURDatabase in the stack.
tableYesAthena table. For a legacy CUR, usually the database name without the athenacurcfn_ prefix.
accountYesAWS account ID that holds the CUR.
authorizerYesHow OpenCost authenticates. See Authorization.
workgroupNoAthena workgroup. Defaults to primary.
catalogNoAthena data catalog. Defaults to AwsDataCatalog.
resultReuseMaxAgeMinutesNoReuse Athena query results for this many minutes. Useful when many clusters run the same query.

Authorization​

The authorizer block accepts one of four authorizerType values. Whichever you choose, attach the Cloud Costs IAM policy to the resulting identity.

Access Key​

AWSAccessKey uses a long-lived IAM user access key. In the IAM console, open Access Management > Users, select the OpenCost user, and choose Security Credentials > Create Access Key:

{
"authorizerType": "AWSAccessKey",
"id": "AKIAIOSFODNN7EXAMPLE",
"secret": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
}

AWSServiceAccount uses the pod's ambient credentials — an IRSA role, EKS Pod Identity, or EC2 instance profile — so no secrets are stored in the file:

{
"authorizerType": "AWSServiceAccount"
}

To set up IRSA:

  1. Register an IAM OIDC provider for the cluster, if you have not already:

    eksctl utils associate-iam-oidc-provider --cluster my-cluster --approve
  2. Create an IAM role the OpenCost ServiceAccount can assume. The OIDC host is the cluster's OIDC issuer URL without https://.

    Trust policy
    {
    "Version": "2012-10-17",
    "Statement": [
    {
    "Effect": "Allow",
    "Principal": {
    "Federated": "arn:aws:iam::123456789012:oidc-provider/oidc.eks.us-east-1.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE"
    },
    "Action": "sts:AssumeRoleWithWebIdentity",
    "Condition": {
    "StringEquals": {
    "oidc.eks.us-east-1.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE:sub": "system:serviceaccount:opencost:opencost",
    "oidc.eks.us-east-1.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE:aud": "sts.amazonaws.com"
    }
    }
    }
    ]
    }
  3. Annotate the OpenCost ServiceAccount in your Helm values:

    serviceAccount:
    create: true
    annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/OpenCostRole

Assume Role​

AWSAssumeRole assumes roleARN using another authorizer — use it to read a CUR in a different AWS account. The wrapped identity needs sts:AssumeRole on roleARN:

{
"authorizerType": "AWSAssumeRole",
"roleARN": "arn:aws:iam::123456789012:role/OpenCostRole",
"authorizer": {
"authorizerType": "AWSServiceAccount"
}
}

Web Identity​

AWSWebIdentity exchanges an OIDC token from outside AWS for AWS credentials — use it when OpenCost runs on another cloud:

{
"authorizerType": "AWSWebIdentity",
"roleARN": "arn:aws:iam::123456789012:role/OpenCostRole",
"identityProvider": "Google",
"tokenRetriever": {
"aud": "sts.amazonaws.com"
}
}

Cloud Costs IAM policy​

The identity needs to run Athena queries, read Glue table metadata, read and write the query results bucket, and read the CUR bucket (my-cur-bucket below). The Glue statement is required: OpenCost queries information_schema.columns to detect the CUR version, and without it no Cloud Costs are returned.

IAM policy
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Athena",
"Effect": "Allow",
"Action": [
"athena:StartQueryExecution",
"athena:GetQueryExecution",
"athena:GetQueryResults",
"athena:StopQueryExecution",
"athena:GetWorkGroup"
],
"Resource": "arn:aws:athena:us-east-1:123456789012:workgroup/primary"
},
{
"Sid": "Glue",
"Effect": "Allow",
"Action": [
"glue:GetDatabase",
"glue:GetDatabases",
"glue:GetTable",
"glue:GetTables",
"glue:GetPartitions"
],
"Resource": [
"arn:aws:glue:us-east-1:123456789012:catalog",
"arn:aws:glue:us-east-1:123456789012:database/athenacurcfn_my_cur",
"arn:aws:glue:us-east-1:123456789012:table/athenacurcfn_my_cur/my_cur_2_0"
]
},
{
"Sid": "QueryResultsBucket",
"Effect": "Allow",
"Action": ["s3:GetBucketLocation", "s3:ListBucket"],
"Resource": "arn:aws:s3:::aws-athena-query-results-my-cur"
},
{
"Sid": "QueryResultsObjects",
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject"],
"Resource": "arn:aws:s3:::aws-athena-query-results-my-cur/*"
},
{
"Sid": "CurBucket",
"Effect": "Allow",
"Action": ["s3:GetBucketLocation", "s3:ListBucket"],
"Resource": "arn:aws:s3:::my-cur-bucket"
},
{
"Sid": "CurObjects",
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-cur-bucket/*"
}
]
}

Deploy the configuration​

Load the cloud-integration.json into a Kubernetes secret in your opencost namespace.

kubectl create secret generic cloud-costs --from-file=./cloud-integration.json --namespace opencost

Update your local OpenCost Helm values file to match the name of the secret and enable Cloud Costs:

opencost:
cloudIntegrationSecret: cloud-costs
cloudCost:
enabled: true

You may refer to the Cloud Costs documentation for configuring Cloud Costs for multiple accounts and cloud service providers.

Verifying​

Look for AthenaIntegration[...]: GetCloudCost: in the OpenCost logs, then check that /model/cloudCost returns data. If nothing is attributed to Kubernetes, confirm your CUR 2.0 export includes resource_tags.

Install OpenCost​

Helm is the preferred installation method for OpenCost.

Using the OpenCost Helm Chart​

You may check out the source for the OpenCost Helm Chart or you may install the Helm chart directly to your Kubernetes cluster. Review the values.yaml for the settings available for customization. With your custom settings in the Helm values file local.yaml, install OpenCost:

helm install opencost --repo https://opencost.github.io/opencost-helm-chart opencost \
--namespace opencost -f local.yaml

Updating OpenCost via Helm​

Upgrading the Helm chart version or updating settings may be done with the following:

helm upgrade opencost --repo https://opencost.github.io/opencost-helm-chart opencost \
--namespace opencost -f local.yaml

Installing with the OpenCost Manifest​

Installing from the OpenCost manifest is supported on AWS.

OpenCost standalone Kubernetes manifest installation is no longer supported. Use the OpenCost Helm chart for installation and upgrades:

helm repo add opencost https://opencost.github.io/opencost-helm-chart
helm repo update
helm install opencost opencost/opencost --namespace opencost --create-namespace
Documentation Distributed under CC BY 4.0.  The Linux Foundation® (TLF) has registered trademarks and uses trademarks. For a list of TLF trademarks, see: Trademark Usage.