Skip to main content
Tutorial 24 covered cost_limit on component blocks — constraining the total cost of a component abstraction. The same attribute works on individual unit blocks, giving you per-resource cost governance without needing to wrap resources in components. A multi-AZ RDS instance with cost_limit = 200 will block ubx apply if the AI estimates it will cost more than $200/month. A Lambda function with cost_limit = 20 catches the mistake of accidentally setting a very high memory allocation. Like the component variant, the limit is only enforced when ubx plan --cost is run with UBX_AI_API_KEY set — plain ubx validate always passes, ensuring cost governance doesn’t break CI pipelines that don’t have AI configured.

What you’ll learn

  • How cost_limit on a unit block enforces a per-resource spend ceiling
  • The three-tier behaviour: validate (always passes), plan (warns), apply (blocks)
  • How to set different limits for different resource tiers

Why this matters

Per-resource cost_limit is infrastructure budgeting at the source level. A cost_limit = 200 on an RDS instance communicates the expected cost envelope to every engineer who reads the .iac file — and enforces it automatically when AI evaluation is enabled.

The source code

# cost_limit sets a maximum monthly spend estimate for this resource.
# Without --cost: stored as metadata, never evaluated.
# With --cost + UBX_AI_API_KEY: warns on ubx plan, errors on ubx apply if exceeded.
unit "aws_rds_instance" "db" {
  cost_limit = 200.00

  engine                  = "postgres"
  instance_class          = "db.t3.medium"
  identifier              = "myapp-${input.env}-db"
  storage_encrypted       = true
  backup_retention_period = 7
  multi_az                = true
}

# A smaller resource with a tighter ceiling.
unit "aws_s3_bucket_v2" "logs" {
  cost_limit = 20.00

  bucket = "myapp-${input.env}-logs"
}

How it works

1

cost_limit is parsed and stored as resource metadata

cost_limit = 200.00 is recorded on the IRUnit node as a metadata field. It doesn’t appear in the generated Pulumi code — it’s a ubx-level constraint only.
2

ubx plan --cost evaluates each resource against its limit

With UBX_AI_API_KEY set, ubx plan --cost asks the AI to estimate the monthly cost of each resource that has a cost_limit. The estimate is compared to the limit and shown in the plan output with a pass/warn/fail indicator.
3

Exceeded limits block ubx apply

If the AI estimates a resource’s cost exceeds its cost_limit, ubx apply is blocked. The apply fails before any resources are created or modified. Fix the configuration (reduce instance_class, disable multi_az, etc.) or raise the limit, then re-plan.

Behaviour by command

Commandcost_limit behaviour
ubx validateParsed, stored as metadata — never evaluated, always passes
ubx planParsed, stored — no AI call, no evaluation
ubx plan --costAI evaluates each resource, warns if cost_limit exceeded
ubx applyBlocked if a prior --cost check found any limit exceeded
ubx apply (no prior cost check)Runs normally — no cost enforcement without --cost

Common mistakes

Changing instance_class = "db.t3.medium" to instance_class = "db.r6g.4xlarge" will almost certainly exceed a cost_limit = 200 limit. The AI estimate for an r6g.4xlarge multi-AZ RDS instance in us-east-1 is approximately $1,200/month. Always run ubx plan --cost after changing instance types.
Set cost_limit as self-documenting infrastructure budgeting — even if you never run --cost in CI. cost_limit = 200 on a production database communicates the expected cost envelope to every engineer who reads the file. It’s more informative than a comment and enforced when you need it.

Run it

ubx validate                              # always passes — cost_limit not evaluated
ubx plan                                  # no cost evaluation
UBX_AI_API_KEY=xxx ubx plan --cost        # evaluates cost_limit for each resource
# Try exceeding a limit:
UBX_AI_API_KEY=xxx ubx plan --cost --var instance_class=db.r6g.4xlarge

What you learned

cost_limit on a unit block sets a per-resource monthly spend ceiling
Without --cost, cost_limit is metadata only — ubx validate always passes
ubx apply is blocked if a prior --cost check found any limit exceeded

Next steps

Security scan

Enforce security rules at validate time

AI plan summary

Plain-English plan summaries and cost estimates