The Problem: Cost Surprises After Deployment
The traditional cloud cost workflow is backwards: engineers write Terraform, deploy to production, and discover the cost impact weeks later when the bill arrives. By then, the architecture is live, dependencies have formed, and changing it is expensive in both engineering time and migration risk.
Shift-left cost visibility flips this: show engineers the monthly cost delta of their pull request before they merge. A $2,000/month increase is easy to optimize when it's still in code review. It's much harder to address six weeks after deployment.
Infracost Setup
# Install Infracost CLI
brew install infracost # macOS
# Or:
curl -fsSL https://raw.githubusercontent.com/infracost/infracost/master/scripts/install.sh | sh
# Authenticate (free for open source, paid for teams)
infracost auth login
# Register for API key (free tier: unlimited OSS projects)
infracost configure set api_key YOUR_API_KEY
# Run against a Terraform project
cd /path/to/terraform
infracost breakdown --path .
# Output:
# Name Monthly Qty Unit Monthly Cost
# aws_instance.web
# ββ Instance usage (Linux/UNIX, on-demand, m5.xlarge) 730 hours $140.16
# ββ root_volume: General Purpose SSD storage (gp2) 50 GB $5.00
# ββ root_volume: Provisioned IOPS 0 IOPS $0.00
# aws_rds_instance.main
# ββ Database instance (Multi-AZ, db.r6g.2xlarge) 730 hours $583.38
# OVERALL TOTAL $728.54
GitHub Actions: Cost Comment on Every PR
# .github/workflows/infracost.yml
name: Infracost Cost Estimate
on:
pull_request:
paths:
- '**.tf'
- '**.tfvars'
jobs:
infracost:
name: Infracost PR Comment
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- name: Checkout PR branch
uses: actions/checkout@v4
- name: Setup Infracost
uses: infracost/actions/setup@v3
with:
api-key: {{ secrets.INFRACOST_API_KEY }}}
- name: Checkout base branch for comparison
uses: actions/checkout@v4
with:
ref: {{ github.event.pull_request.base.ref }}}
path: /tmp/base-branch
- name: Generate cost estimate for base branch
run: |
infracost breakdown --path /tmp/base-branch --format json --out-file /tmp/infracost-base.json --terraform-var-file=terraform.tfvars
- name: Generate cost estimate for PR branch
run: |
infracost breakdown --path . --format json --out-file /tmp/infracost-pr.json --terraform-var-file=terraform.tfvars
- name: Post diff as PR comment
run: |
infracost diff --path /tmp/infracost-pr.json --compare-to /tmp/infracost-base.json --format diff
- name: Post cost comment to PR
uses: infracost/actions/comment@v3
with:
path: /tmp/infracost-pr.json
compare-to: /tmp/infracost-base.json
behavior: update # Update existing comment instead of creating new ones
Example PR Comment Output
## Infracost estimate
| Monthly cost change | Details |
|---------------------|---------|
| +$2,847/month | 3 resources changed |
### Changed resources
aws_instance.web
- instance_type: t3.small -> m5.2xlarge
Monthly cost: $15.18 -> $280.32 (+$265.14/month)
aws_db_instance.main
- instance_class: db.t3.medium -> db.r6g.2xlarge
Monthly cost: $48.91 -> $583.38 (+$534.47/month)
aws_elasticache_cluster.cache
+ NEW RESOURCE added
Monthly cost: +$2,047.39/month (dax.r6g.4xlarge Γ 3)
---
Previous total: $1,247.58/month
New total: $4,094.44/month
Difference: +$2,846.86/month (+228%)
@reviewer: This PR adds $2,847/month. Please confirm this is intentional.
Tip: Consider dax.r6g.large Γ 3 ($374/month) to start.
Policy Enforcement with OPA (Open Policy Agent)
# infracost-policy.rego
# Block PRs that increase monthly cost by more than $500
package infracost
# Deny if monthly cost increase exceeds threshold
deny[msg] {
baseline := input.projects[_].breakdown.totalMonthlyCost
pr := input.projects[_].diff.totalMonthlyCost
increase := pr - baseline
increase > 500
msg := sprintf(
"Monthly cost increase of $%.2f exceeds the $500 limit. Requires FinOps team approval.",
[increase]
)
}
# Warn about expensive instance types
warn[msg] {
resource := input.projects[_].breakdown.resources[_]
resource.resourceType == "aws_instance"
contains(resource.metadata.calls[_].arguments.instance_type, "x1e")
msg := sprintf(
"Resource %s uses x1e instance family (memory-optimized, high cost). Consider r6g.",
[resource.name]
)
}
# Block specific expensive instance families
deny[msg] {
resource := input.projects[_].breakdown.resources[_]
resource.resourceType == "aws_instance"
instance_type := resource.metadata.calls[_].arguments.instance_type
blocked := ["p4d", "p3dn", "x1e", "x2gd"]
blocked[_] == split(instance_type, ".")[0]
msg := sprintf(
"Instance type %s is in the blocked list. Use p3 or g4dn for GPU, r6g for memory.",
[instance_type]
)
}
# Apply OPA policy in CI
infracost breakdown --path . --format json --out-file infracost.json
# Install OPA
brew install opa
# Evaluate policy
opa eval --data infracost-policy.rego --input infracost.json "data.infracost.deny" --format pretty
# Fail CI if any deny rules triggered
VIOLATIONS=$(opa eval --data infracost-policy.rego --input infracost.json "count(data.infracost.deny)" --format raw)
if [ "$VIOLATIONS" -gt "0" ]; then
echo "Cost policy violations found. See details above."
exit 1
fi
Infracost Cloud (Team Features)
Infracost Cloud (paid tier) adds:
1. Central dashboard: All repos, all PRs, cost trends
2. Slack/Teams notifications: Alert team when cost spike > threshold
3. FinOps policies: No-code policy rules (max instance type, max cost increase)
4. Guardrails: Auto-fail PRs that violate policies
5. Budget tracking: Per-project/team cost forecasting
Pricing:
Open source CLI: Free (always)
Team: $99/month for up to 25 engineers
Business: $499/month (SSO, audit logs, SLA)
ROI calculation:
If the team ships 2 PRs/month with unintended $500+ cost increases,
and each takes 4 engineer-hours to remediate post-deployment:
Cost of remediation: 2 Γ 4 Γ $100 = $800/month engineering time
+ wasted cloud spend until fixed: $1,000/month
Total monthly waste: $1,800/month
Infracost Team: $99/month
Monthly ROI: $1,701 (1718% ROI)
Conclusion
Infracost brings cloud cost into the development workflow where it belongs β at code review time, not at invoice time. The GitHub Actions integration takes under an hour to set up and immediately adds cost visibility to every infrastructure PR. OPA policy enforcement makes cost guardrails automatic rather than manual review processes.
The highest-leverage use is setting a cost increase threshold that triggers a mandatory FinOps review. This one workflow change prevents the architectural decisions that create years of unnecessary cloud spend.
Priya Sharma
Full-Stack Developer and open-source contributor with a passion for performance and developer experience.