Infracost Tutorial: Add Terraform Cost Estimates to GitHub PRs
Learn how to use Infracost, Terraform, and GitHub Actions to detect expensive cloud changes before deployment
Infracost Tutorial: Add Terraform Cost Estimates to GitHub PRs
Learn how to use Infracost, Terraform, and GitHub Actions to detect expensive cloud changes before deployment
Photo by Bermix Studio on Unsplash
In many teams, engineers create infrastructure without knowing the exact cost impact. For example, changing an EC2 instance from t3.medium to m7i.4xlarge.
What if your pull request could warn you before merging:
“This change will increase monthly costs by $840.”
Infracost solves this by adding cost visibility into the development workflow. It turns infrastructure changes into cost estimates during code review, so teams catch expensive decisions early instead of after deployment.
In this article, you’ll build a complete demo from scratch using Terraform, GitHub Actions, and Infracost. By the end, you’ll have automated PR cost diffs, FinOps policy checks, and cost guardrails running directly in your CI/CD pipeline.
You can access this story for free here.
New to Terraform? Before diving in, check out Terraform Quickstart— a beginner-friendly ebook that walks you through Terraform fundamentals so you can follow this tutorial with confidence.
Infracost Overview
FinOps is a practice that helps teams make informed decisions about cloud spending by connecting infrastructure work with cost visibility and accountability.
Infracost is a FinOps tool for Infrastructure as Code workflows. It integrates with tools like Terraform, OpenTofu, Terragrunt, AWS CloudFormation, and AWS CDK.
Instead of changing your workflow, it plugs into existing pipelines and shows the cost impact of infrastructure changes during development and code review. This helps teams catch expensive changes early and keep cloud spending under control.
Infracost uses public cloud pricing data from AWS, Azure, and Google Cloud to estimate monthly costs from your infrastructure definitions.
The core CLI is open source and free for individuals and small teams. Infracost Cloud adds pull request comments, policy checks, and governance features, with a free tier and paid plans for larger organizations that need advanced FinOps controls like dashboards, audit logs, and organization-wide policies.
A Complete Infracost Demo with Terraform and GitHub Actions
Prerequisites
- Terraform CLI installed
- A GitHub account
- A free Infracost account (we’ll create it during setup)
The idea of this exercise
The workflow looks like this:
- A developer opens a PR with Terraform changes.
- GitHub Actions runs
terraform planfor the pull request branch. - Infracost reads the plan and calculates the cost diff vs the base.
- Infracost posts a comment on the PR showing the breakdown.
- If the cost increase exceeds your configured threshold, the pipeline fails — blocking the merge until a team lead reviews it.
Step 1 — Install Infracost and Get an API Key
Install the CLI:
curl -fsSL https://raw.githubusercontent.com/infracost/infracost/master/scripts/install.sh | sh
infracost --version
Then authenticate to get your free API key:
infracost auth login
This opens a browser. Follow the steps to sign up for free. You can also use your GitHub account. The key is stored locally and never expires on the free tier.
Step 2 — Create the Terraform Project
Create a working directory and add a simple main.tf with an EC2 instance and an RDS database. We’ll use mock AWS credentials since we only need to generate a Terraform plan and won’t apply anything.
Terraform still validates the provider during planning, but Infracost only reads the plan output. That makes mock credentials enough for this demo.
mkdir ~/infracost-demo && cd ~/infracost-demo
This is the content of the main.tf:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "us-east-1"
skip_credentials_validation = true
skip_requesting_account_id = true
skip_metadata_api_check = true
access_key = "mock_access_key"
secret_key = "mock_secret_key"
}
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
root_block_device {
tags = {
Environment = "Stage"
Service = "web"
}
}
tags = {
Name = "demo-web-server"
Environment = "Stage"
Service = "web"
}
}
resource "aws_db_instance" "postgres" {
engine = "postgres"
instance_class = "db.t3.medium"
allocated_storage = 100
db_name = "appdb"
username = "admin"
password = "notreal123"
skip_final_snapshot = true
tags = {
Name = "demo-postgres"
Environment = "Stage"
Service = "database"
}
}
In production environments, never hardcode database credentials in Terraform files. Use secret managers or CI/CD secret injection instead. We use a placeholder password here only for the demo.
Step 3 — See Your First Cost Breakdown Locally
Before wiring up GitHub Actions, let’s run Infracost locally so you understand what it produces.
terraform init
terraform plan -out=tfplan.binary
terraform show -json tfplan.binary > plan.json
infracost breakdown --path=plan.json
This provides a detailed, resource-by-resource cost estimate of your cloud infrastructure before you actually deploy it. You’ll see an output like this:
Name Monthly Qty Unit Monthly Cost
aws_db_instance.postgres
├─ Database instance (on-demand, Single-AZ, db.t3.medium) 730 hours $52.56
└─ Storage (general purpose SSD, gp2) 100 GB $11.50
aws_instance.web
├─ Instance usage (Linux/UNIX, on-demand, t3.micro) 730 hours $7.59
└─ root_block_device
└─ Storage (general purpose SSD, gp2) 8 GB $0.80
OVERALL TOTAL $72.45
Now let’s simulate a developer making an expensive change — upgrading both resources:
Edit main.tf and change these values:
instance_type = "t3.micro"→"t3.xlarge"instance_class = "db.t3.medium"→"db.r5.2xlarge"
Then generate the new plan and run a diff:
# Create a new plan file for the updated infrastructure
terraform plan -out=tfplan2.binary
# Convert the binary plan into JSON format (required by Infracost)
terraform show -json tfplan2.binary > plan2.json
# Baseline cost (original configuration)
infracost breakdown --path=plan.json \
--format=json \
--project-name=demo \
--out-file=infracost-base.json
# New cost after infrastructure changes
infracost breakdown --path=plan2.json \
--format=json \
--project-name=demo \
--out-file=infracost-new.json
# Show cost difference between baseline and new plan
infracost diff --path=infracost-new.json --compare-to=infracost-base.json
The output shows exactly what changed and what it costs:
Project: demo
~ aws_db_instance.postgres
+$677 ($64 → $742)
~ Database instance: db.t3.medium → db.r5.2xlarge +$677
~ aws_instance.web
+$114 ($8 → $122)
~ Instance usage: t3.micro → t3.xlarge +$114
Monthly cost change for demo
Amount: +$791 ($72 → $864)
Percent: +1,092%
Step 4 — Push to GitHub
Revert main.tf back to the small instance sizes (t3.micro and db.t3.medium) — this becomes your main branch baseline.
Add a .gitignore so plan files and state don't end up in git:
cat > .gitignore << 'EOF'
*.binary
*.json
*.html
.terraform/
.terraform.lock.hcl
EOF
Create a repo on GitHub, for example, infracost-demo. Then in your project’s directory run:
git init
git add main.tf .gitignore
git commit -m "initial: t3.micro + db.t3.medium"
git branch -M main
git remote add origin https://github.com/YOUR_USERNAME/infracost-demo.git
git push -u origin main
Step 5 — Add the GitHub Actions Workflow
mkdir -p .github/workflows
cat > .github/workflows/infracost.yml << 'EOF'
name: Infracost
on:
pull_request:
types: [opened, synchronize, closed]
jobs:
infracost:
name: Infracost diff
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- name: Checkout PR branch
uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_wrapper: false
- name: Terraform init
run: terraform init
env:
AWS_ACCESS_KEY_ID: mock_access_key
AWS_SECRET_ACCESS_KEY: mock_secret_key
- name: Terraform plan
run: |
terraform plan -out=tfplan.binary
terraform show -json tfplan.binary > plan.json
env:
AWS_ACCESS_KEY_ID: mock_access_key
AWS_SECRET_ACCESS_KEY: mock_secret_key
- name: Setup Infracost
uses: infracost/actions/setup@v3
with:
api-key: ${{ secrets.INFRACOST_API_KEY }}
- name: Infracost diff
run: |
infracost diff \
--path=plan.json \
--format=json \
--out-file=/tmp/infracost.json
- name: Post Infracost comment
run: |
infracost comment github \
--path=/tmp/infracost.json \
--repo=$GITHUB_REPOSITORY \
--github-token=${{ github.token }} \
--pull-request=${{ github.event.pull_request.number }} \
--behavior=update
EOF
git add .github/
git commit -m "add infracost github actions workflow"
git push
Step 6 — Add Your API Key as a Repo Secret
This step lets GitHub Actions authenticate with Infracost Cloud. The API key connects your CI pipeline to your Infracost account so it can fetch pricing data and post cost comments on pull requests. Without it, the workflow can still run Terraform, but Infracost won’t be able to generate or publish cost estimates.
Get your key:
infracost configure get api_key
Then, in GitHub, go to your repo. Navigate through Settings → Secrets and variables → Actions → New repository secret.
Create this key-value pair:
- Name:
INFRACOST_API_KEY - Value: paste your key from the command above
You can close this window. You should see this output in the Terminal:
Your account has been authenticated. Run Infracost on your Terraform project by running:
infracost breakdown --path=.
Step 7 — Open the Expensive PR
Now create a branch with the costly change:
git checkout -b expensive-infra
Edit main.tf — change the two instance sizes back to the large ones:
instance_type = "t3.xlarge"instance_class = "db.r5.2xlarge"
Push and open a PR:
git add main.tf
git commit -m "scale up: t3.xlarge + db.r5.2xlarge"
git push -u origin expensive-infra
Go to GitHub and open a pull request from expensive-infra → main. Within about 60 seconds, the Infracost bot posts its first comment.
Understanding the Pipeline and the PR Comments
You’ll notice the pipeline will fail. Open the GitHub Actions tab and check the logs — it shows exactly what went wrong and what you need to fix.
Error: Governance check failed:
Comment posted to GitHub
- finops policy check failed: EC2 - consider using Graviton instances
- finops policy check failed: RDS - consider using latest generation instances for r family instances
- guardrail check failed: Cost increased by $864, threshold was $250. Review the estimate to ensure it meets your expectations. If you are unsure what to do, check with your team lead.
FinOps Policy Violations
Infracost checks your resources against AWS Well-Architected Framework recommendations. For example, it will flag:
- Using
db.r5whendb.r7i(newer generation, same price or cheaper) is available - Using
t3when Graviton (t4g) instances offer better price/performance
You can fix these issues by modifying the main.tffile accordingly.
Guardrails and Cost Thresholds
The pipeline fails because of this:
❌ Significant cost increase: please review
Cost increased by $840, threshold was $250.
Review the estimate to ensure it meets your expectations.
If you are unsure what to do, check with your team lead.
The $250 threshold is the default guardrail that Infracost Cloud pre-configures on new accounts. You can change it at **dashboard.infracost.io → Governance → Guardrails**. In a real team , you’d tune this per repo — tighter for dev environments, more generous for production scaling repos. You can also switch from a flat dollar threshold to a percentage-based one, which works better when the baseline cost varies.
The key idea is simple: engineers see the financial impact of infrastructure changes during code review instead of after deployment.

Infracost bot comments on PR.
Conclusion
In this article, you built a complete Terraform cost visibility workflow with Infracost and GitHub Actions. You learned how to estimate infrastructure costs before deployment, review cost diffs directly in pull requests, and add FinOps guardrails into your CI/CD pipeline.
For platform engineering and DevOps teams managing cloud infrastructure at scale, this creates a much faster feedback loop between engineering decisions and cloud spending.
All code from this article is available at my github repo.
Thanks for reading, and see you next time!
You might also like:
메타데이터
- post_id
- 4cccb4e0e270
- slug
- infracost-finops-terraform-cost-estimates-to-github-prs-4cccb4e0e270
- url
- https://medium.com/curious-devs-corner/infracost-finops-terraform-cost-estimates-to-github-prs-4cccb4e0e270
- canonical_url
- https://medium.com/curious-devs-corner/infracost-finops-terraform-cost-estimates-to-github-prs-4cccb4e0e270
- author_url
- https://medium.com/@kirshiyin
- status
- ok
- fetched_at
- 2026-07-09 13:13:48