Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Infracost: Estimate Terraform Costs in CI

By Kokil Thapa | Last reviewed: September 2026

Cloud bills often spike after a Terraform merge, not during planning. Infracost: Estimate Terraform Costs in CI closes that gap by turning each pull-request plan into a readable monthly cost diff before anyone approves the change. If you already run Terraform CI/CD with GitHub Actions or GitLab pipelines, adding cost feedback takes minutes and saves hours of FinOps cleanup later. This guide walks through setup, pipeline wiring, threshold gates, and the production mistakes I see on real infrastructure repos.

What is Infracost and why should Terraform run it in CI?

Infracost is an open-source CLI that reads Terraform plan output and estimates monthly cloud spend. It does not replace your cloud provider billing console. It gives engineers a fast, consistent preview at review time.

The workflow is straightforward. Terraform produces a binary plan file. Infracost converts that plan to JSON, looks up unit prices, and renders a diff: added cost, removed cost, and net monthly change. That output lands as a PR comment, a pipeline artefact, or a failing job when spend crosses a threshold.

On teams where I maintain GitLab CI pipelines for application and infrastructure repos, cost visibility was always an afterthought. Someone merged an RDS size bump or an extra NAT gateway. Finance noticed three weeks later. Infracost in CI moves that conversation to the pull request, where the change is cheap to revert.

Infracost in a Terraform CI Pull RequestDeveloperOpens PRCI Runnerterraform planInfracostCost diffReviewersApprove or blockWhat Infracost reads and outputsPlan JSONResource types + countsPrice bookAWS, Azure, GCP ratesMonthly diff+$142/mo exampleEstimates are directional — validate against provider billing for budgets
Infracost: Estimate Terraform Costs in CI by parsing plan output and surfacing monthly spend diffs at pull-request review time.

Infracost supports AWS, Azure, Google Cloud, and dozens of SaaS resources through the Terraform provider ecosystem. It works with infrastructure as code with Terraform, OpenTofu, and Terragrunt wrappers. The CLI is free and open source. Cloud Dashboard and policy features add team management when you need central guardrails.

FinOps is not only a finance task. Engineers choose instance sizes, replica counts, and data transfer paths. Putting cost data beside the plan output makes trade-offs visible without opening a spreadsheet. That aligns with broader FinOps and cloud cost optimization practices your org may already track.

How do you install and configure Infracost for Terraform CI?

Start on a developer machine before you wire CI. You need Terraform (or OpenTofu), the Infracost CLI, and a registered API key from the Infracost Cloud dashboard or self-hosted setup.

Install the CLI on Linux or macOS

Official install scripts pin a release. On Ubuntu 22.04 or 24.04—the same base I use for production runners—run:

curl -fsSL https://raw.githubusercontent.com/infracost/infracost/master/scripts/install.sh | sh
infracost --version

Register once per environment:

infracost register

This stores an API key in ~/.config/infracost/credentials.yml. In CI, inject the key from a secret store instead of committing it. Treat it like any other credential you protect with secrets scanning in Git and CI.

Generate a plan file Infracost can parse

Infracost needs a saved plan, not just console output. From your Terraform root module:

terraform init -input=false
terraform plan -out=tfplan.binary
terraform show -json tfplan.binary > plan.json
infracost breakdown --path plan.json

For directory-based runs without a saved plan:

infracost breakdown --path .

The breakdown command prints total monthly cost and per-resource lines. Use infracost diff when you have a baseline branch to compare against. That diff is what you want on pull requests.

Configure currency and usage assumptions

Default currency is USD. Set NPR-friendly reporting by passing --currency NPR where supported, or post-process for finance teams that think in rupees. Usage-based resources—S3 egress, Lambda invocations, CloudWatch logs—need a infracost-usage.yml file with realistic quantities. Without usage files, Infracost assumes minimal defaults and under-estimates variable spend.

version: 0.1
resource_usage:
  aws_lambda_function.my_fn:
    monthly_requests: 500000
    request_duration_ms: 120
  aws_cloudwatch_log_group.app:
    storage_gb: 30
    monthly_data_ingested_gb: 15

Commit usage templates per environment. Update them when traffic patterns change. Pair this with Terraform workspaces and environments so staging assumptions do not mask production scale.

How do you integrate Infracost with GitHub Actions or GitLab CI?

CI integration follows the same four steps: checkout, init, plan, infracost. The official GitHub Action wraps comment posting and S3 cache for baseline plans. GitLab CI uses a shell job with artefacts—similar to pipelines I run for Laravel deploys, but with Terraform state backends instead of Composer.

Terraform CI Pipeline with Infracost StepCheckoutPR branch codeInitProviders + backendPlanSave plan.jsonInfracostDiff + commentGatePass/failParallel jobs you should keep separateCheckov scanSecurity misconfigsInfracost diffSpend visibilityPlan apply gateManual approvalRun cost checks on every plan — not only on apply branches
Typical CI order: Terraform plan first, then Infracost diff, alongside security scans like Checkov before any apply job.

GitHub Actions example

Add a job that runs on pull requests targeting your default branch. Store INFRACOST_API_KEY in GitHub Actions secrets. Official docs live at Infracost GitHub Actions integration.

name: Terraform cost estimate
on:
  pull_request:
    paths:
      - 'infra/**'
      - '.github/workflows/terraform-cost.yml'

jobs:
  infracost:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    steps:
      - uses: actions/checkout@v4

      - uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: 1.9.0

      - name: Terraform init and plan
        working-directory: infra/prod
        run: |
          terraform init -input=false
          terraform plan -out=tfplan.binary -input=false
          terraform show -json tfplan.binary > plan.json
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

      - uses: infracost/actions/setup@v3
        with:
          api-key: ${{ secrets.INFRACOST_API_KEY }}

      - name: Post Infracost comment
        run: |
          infracost comment github --path infra/prod/plan.json \
            --repo ${{ github.repository }} \
            --pull-request ${{ github.event.pull_request.number }} \
            --github-token ${{ secrets.GITHUB_TOKEN }} \
            --behavior update

The --behavior update flag replaces an existing bot comment instead of spamming the thread. That small detail keeps PRs readable on active repos.

GitLab CI example

GitLab fits teams already using GitLab CI pipelines for application deploys. Add a stage before apply:

stages:
  - validate
  - plan
  - cost
  - apply

terraform-plan:
  stage: plan
  image:
    name: hashicorp/terraform:1.9
    entrypoint: [""]
  script:
    - cd infra/staging
    - terraform init -input=false
    - terraform plan -out=tfplan.binary -input=false
    - terraform show -json tfplan.binary > plan.json
  artifacts:
    paths:
      - infra/staging/plan.json
    expire_in: 1 week

infracost-diff:
  stage: cost
  image: infracost/infracost:ci-0.10
  needs: [terraform-plan]
  script:
    - infracost breakdown --path infra/staging/plan.json --format json --out-file infracost.json
    - infracost output --path infracost.json --format table
    - infracost diff --path infra/staging/plan.json --compare-to main
  variables:
    INFRACOST_API_KEY: $INFRACOST_API_KEY

For Azure DevOps users, the same plan-json pattern applies inside Terraform with Azure DevOps Pipelines. Swap the comment helper for the Azure DevOps extension or publish artefacts to a cost dashboard.

State, credentials, and remote backends

CI plans fail silently when state backends are misconfigured. Confirm your runner can reach S3, Azure Blob, or Terraform Cloud. Lock files must be committed. Partial backend config belongs in CI variables, not in git. Read how to manage Terraform state safely before you automate cost gates on production paths.

How do you set cost thresholds and policy gates in CI?

Visibility alone helps mature teams. Regulated or budget-sensitive orgs often need hard stops. Infracost supports percentage and absolute monthly diff thresholds through CLI flags and Cloud Policy features.

CLI threshold on pull requests

Fail the job when monthly increase exceeds a dollar limit:

infracost diff --path plan.json \
  --compare-to infracost-base.json \
  --format json > diff.json

TOTAL=$(jq '.diffTotalMonthlyCost | tonumber' diff.json)
LIMIT=100

if awk "BEGIN {exit !($TOTAL > $LIMIT)}"; then
  echo "Monthly cost increase $TOTAL exceeds limit $LIMIT"
  exit 1
fi

That pattern mirrors code coverage gates in CI. Treat cost like any other quality metric with a defined pass line.

Infracost Cloud Policy

Cloud Policy centralises rules: block any EC2 instance larger than a threshold, flag resources without tags, or require approval when diff exceeds Rs 50,000/month (~USD 375). Policies run in CI through the same API key and report pass/fail in the dashboard. For multi-account setups, see multi-cloud cost management and FinOps for how estimates fit beside real billing exports.

ApproachBest forEnforcementSetup effort
PR comment onlySmall teams, early adoptionAdvisoryLow — one job
Shell threshold (jq/awk)Single repo, fixed budgetHard fail in CILow — ~10 lines
Infracost Cloud PolicyMany repos, tag rules, approvalsCentral policy engineMedium — dashboard config
Custom FinOps webhookExisting chargeback systemsVariesHigh — integration work
Advisory vs Enforced Cost GatesAdvisory commentPR shows +$890/moMerge still allowedGood for learning phaseEnforced thresholdLimit: +$100/moPipeline fails at gateRequires fix or overrideCI exit code 1matureStart advisory for one sprint, then enable hard gates on production paths
Most teams begin with Infracost PR comments, then add enforced thresholds once baseline usage files are trustworthy.

Document override paths. Emergencies happen—a hotfix may need a larger instance for 48 hours. Require a finance or platform approver label when the gate fails instead of disabling the job permanently.

What are common Infracost CI mistakes and how do you fix them?

Most failures I troubleshoot are configuration gaps, not Infracost bugs. The CLI is only as accurate as the plan and usage data you feed it.

  1. Planning without remote state access. CI produces empty plans when credentials or backends are wrong. Cost shows zero change while production would change. Fix backend auth first; cost second.
  2. Skipping usage files on metered services. NAT gateways, Lambda, and log storage look cheap with defaults. Add infracost-usage.yml per environment and review quarterly.
  3. Comparing against the wrong baseline. Use --compare-to with a JSON snapshot from main, not stale artefacts. Regenerate baseline on each main merge.
  4. Running only on apply branches. Cost feedback must run on pull requests. Post-merge comments arrive too late.
  5. Ignoring unsupported resources. Check the breakdown for "Unsupported" lines. Custom providers or new resource types may need upstream price entries or manual notes in the PR template.
  6. Treating estimates as invoices. Reserved instances, credits, and enterprise discounts are not in public list prices. Cross-check with Azure cost management or AWS Cost Explorer for budget sign-off.

Run Checkov scans on Terraform in the same pipeline. Security and cost failures often share root causes—oversized public resources, missing autoscaling, orphaned disks.

Infracost CI Troubleshooting FlowCost diff looks wrong?Zero change?Check plan.json sizeToo low?Add usage YAMLGate failed?Review diff tableFix state + credentialsRe-run terraform plan in CIRe-run pipeline after each fixDo not merge until costs passValidate locally with infracost breakdown before pushing CI YAML changes
When Infracost output surprises you, verify plan JSON, usage assumptions, and baseline comparison before changing threshold limits.

Keep a local repro command in your team wiki. Developers should run the same init, plan, and infracost steps CI runs. Paste output into PRs when estimates look off—that saves platform team round trips.

For JSON pipeline artefacts, a quick sanity check through the JSON formatter tool helps confirm diff structure before you write jq gates. Small utilities beat debugging escaped shell strings under pressure.

Key Takeaways

  • Run Infracost on every Terraform pull request plan, not only on apply branches.
  • Save plan JSON in CI, then call infracost breakdown or infracost diff with a current main baseline.
  • Add infracost-usage.yml for metered services so estimates reflect real traffic, not CLI defaults.
  • Start with advisory PR comments, then add shell or Cloud Policy gates once baselines are stable.
  • Pair cost checks with security scans and safe remote state access—the same misconfigs cause both bill shock and risk.
  • Treat Infracost numbers as directional previews; reconcile with provider billing before hard budget commitments.

People Also Ask

Does Infracost work with OpenTofu and Terragrunt?

Yes. Infracost reads Terraform plan JSON regardless of whether you generated it with OpenTofu or wrapped modules with Terragrunt. Point the CLI at the plan file or directory output. Provider support follows the Terraform registry resource types Infracost maintains in its price book.

How accurate are Infracost estimates compared to real cloud bills?

Estimates use public list prices and your usage assumptions. They exclude enterprise discounts, reserved capacity, free tiers beyond defaults, and tax. Use them to compare relative diffs between PRs, not to predict the exact NPR or USD invoice.

Can Infracost fail a CI pipeline when costs increase too much?

Yes. Parse infracost diff JSON with jq or awk and exit non-zero when the monthly delta exceeds your limit. Infracost Cloud Policy offers managed rules if you prefer dashboard-driven enforcement across many repositories.

Do you need Infracost Cloud for CI pull request comments?

You need a free API key for comment features and telemetry, but basic breakdown and diff commands run locally without a paid subscription. Cloud adds policies, dashboards, and team governance when your footprint grows beyond a single repo.

Ship Terraform changes with cost visibility built in

Infracost: Estimate Terraform Costs in CI turns infrastructure review into a finance-aware checkpoint without slowing developers down. Plan, diff, comment, gate—that sequence fits beside the pipelines you already maintain and catches the expensive surprises that used to surface only after merge.

If you want help wiring Terraform CI, FinOps guardrails, or production Linux and cloud administration for Nepal and remote teams, contact us with your repo layout and cloud targets. You can also browse the Adventure Third Pole Trek portfolio entry for an example of Laravel plus infrastructure work shipped end to end, or explore enterprise application development services when cost-aware IaC is part of a larger platform build.

Frequently Asked Questions

Infracost is an open-source CLI that reads Terraform plan output and estimates monthly cloud spend. In CI it parses saved plan JSON, maps resources to public price books, and posts a monthly cost diff on pull requests. That moves FinOps review to merge time, when reverting an expensive RDS bump or extra NAT gateway is still cheap. It does not replace your provider billing console; it gives engineers a consistent preview beside the plan.

On Ubuntu 22.04 or 24.04 runners, install with the official script: curl -fsSL https://raw.githubusercontent.com/infracost/infracost/master/scripts/install.sh | sh, then verify with infracost --version. Register once using infracost register, which stores an API key in ~/.config/infracost/credentials.yml. In CI, inject INFRACOST_API_KEY from your secret store instead of committing credentials. Treat the key like any other pipeline secret and protect it with Git secrets scanning.

Infracost needs a saved plan, not console output alone. From your Terraform root module run terraform init -input=false, terraform plan -out=tfplan.binary -input=false, then terraform show -json tfplan.binary > plan.json. Pass that file to infracost breakdown --path plan.json for totals, or infracost diff when comparing against a baseline branch snapshot. For simpler local checks you can run infracost breakdown --path . on a directory, but CI pipelines should always generate plan JSON from the same init and plan steps production uses.

Add a job triggered on pull_request paths covering your infra folder. Checkout code, use hashicorp/setup-terraform with your version, run init and plan in the working directory, export plan JSON, then use infracost/actions/setup@v3 with INFRACOST_API_KEY from GitHub Actions secrets. Post results with infracost comment github --path plan.json --repo --pull-request --github-token and --behavior update so one bot comment replaces the previous one. Grant pull-requests: write permission so the comment step succeeds.

Split Terraform planning and costing into separate stages. A terraform-plan job using hashicorp/terraform:1.9 runs init, saves tfplan.binary, exports plan.json, and stores it as an artefact. A downstream infracost-diff job using infracost/infracost:ci-0.10 needs that artefact, runs infracost breakdown with JSON output, prints a table via infracost output, then infracost diff --compare-to main. Pass INFRACOST_API_KEY as a protected CI variable. Place the cost stage before apply, alongside validation and security scans.

Yes. Infracost reads Terraform plan JSON whether you generated it with OpenTofu or wrapped modules with Terragrunt. Point the CLI at the plan file or directory output.

Estimates use public list prices and your usage assumptions. They exclude enterprise discounts, reserved capacity, credits, and tax—use them to compare PR diffs, not exact invoices.

Yes. The CLI is free and open source. You need a registered API key for PR comment features; Infracost Cloud adds paid policy and dashboard features for larger teams.

Yes, for metered services. Usage-based resources like S3 egress, Lambda invocations, and CloudWatch logs default to minimal quantities without a usage file, which under-estimates variable spend. Create infracost-usage.yml with version 0.1 and resource_usage entries—for example monthly_requests and request_duration_ms for Lambda, or storage_gb for log groups. Commit templates per environment and update them when traffic patterns change so staging assumptions do not mask production scale.

Run infracost diff --path plan.json --compare-to infracost-base.json --format json, then parse diffTotalMonthlyCost with jq. Compare the monthly delta against a dollar limit using awk or a shell conditional and exit 1 when exceeded, mirroring code coverage gates. For multi-repo governance, Infracost Cloud Policy centralises rules—block oversized EC2 types, flag untagged resources, or require approval when diffs exceed Rs 50,000/month (~USD 375). Document override paths for emergencies so hotfixes are not blocked permanently.

The most common cause is planning without proper remote state access. When CI credentials or backends for S3, Azure Blob, or Terraform Cloud are misconfigured, Terraform produces empty plans and Infracost reports no spend change while production would differ. Fix backend authentication and confirm lock files are committed before trusting cost output. Partial backend config belongs in CI variables, not git. The same misconfiguration that breaks accurate plans often breaks security scans, so validate state access first.

Run it on every Terraform pull request plan, not only on apply branches. Cost feedback must arrive at review time so engineers see added, removed, and net monthly changes before merge. Post-merge comments arrive too late—Finance often notices RDS size bumps or extra NAT gateways weeks after the change shipped. Pair the cost job with init and plan on PRs targeting your default branch, and regenerate baseline snapshots on each main merge for accurate diff comparisons.

Use infracost diff with --compare-to pointing at a JSON snapshot from main, not stale pipeline artefacts. Regenerate the baseline file whenever main merges infrastructure changes. In GitLab CI the example uses --compare-to main; in GitHub Actions the official action handles baseline caching via S3 for comment diffs. Wrong baselines make every PR look expensive or cheap. When estimates surprise you, verify plan JSON, usage assumptions, and baseline comparison before loosening threshold limits.

Check the breakdown output for Unsupported lines. Custom providers or newly released resource types may lack price book entries yet. Add manual notes in your PR template explaining expected spend until upstream coverage lands. Do not treat missing lines as zero cost. Also remember Infracost uses public list prices—reserved instances, enterprise discounts, and credits are not reflected. Cross-check significant changes with AWS Cost Explorer or Azure cost management before hard budget sign-off, and pair cost scans with Checkov in the same pipeline since oversized public resources often fail both checks.

Start with advisory PR comments only—low setup effort and no hard enforcement while teams learn the workflow. Once infracost-usage.yml baselines per environment are trustworthy, add shell threshold gates with jq and awk for single-repo budget limits, or Infracost Cloud Policy when many repos need tag rules and central approvals. Most failures are configuration gaps, not CLI bugs. Keep a local repro command in team docs—init, plan, and infracost steps matching CI—so developers paste output when estimates look off and platform teams avoid unnecessary round trips.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: