Use ops0 cost estimation, policies, Kubernetes cost analysis, and discovery to control cloud spending before and after deployments.
Cost estimation runs automatically every time you trigger a plan. No setup required.
When you run a plan, ops0 calculates the estimated monthly cost of the planned changes using current cloud provider pricing:
Plan: 3 to add, 1 to change, 0 to destroy
Cost Estimation
─────────────────────────────────────────────────────────
│ Resource │ Type │ Monthly │
├────────────────────────────┼──────────────┼────────────┤
│ aws_instance.app_server │ t3.xlarge │ +$121.47 │
│ aws_db_instance.postgres │ db.r5.large │ +$172.80 │
│ aws_nat_gateway.main │ per-hour │ +$32.40 │
│ aws_instance.bastion │ t3.micro │ +$8.47 │
├────────────────────────────┼──────────────┼────────────┤
│ Total change │ │ +$335.14 │
│ New monthly estimate │ │ $1,850.97 │
─────────────────────────────────────────────────────────
Cost estimates use on-demand prices from the cloud provider's public pricing API. Reserved instances, Savings Plans, committed use discounts, and spot pricing are not factored in — your actual bill may be lower.
| Included | Not included |
|---|---|
| EC2/Compute instance hours | Data transfer costs |
| RDS/Cloud SQL/Azure SQL | Bandwidth charges |
| NAT Gateway hours | Support plan charges |
| Load balancer hours | Marketplace software fees |
| Storage (S3/GCS/Azure Blob) | Reserved/committed discounts |
| Kubernetes node costs | Spot/preemptible savings |
Set a monthly budget for your organization so ops0 notifies you when spending approaches the limit:
Go to Settings → Budget.
Enter your monthly budget in USD. ops0 tracks estimated spending across all projects.
Set alerts at 80% and 100% of budget. Notifications go to your configured Slack or email channels.
Check the budget dashboard monthly to track trends and identify which projects are driving the most spend.
Policies can inspect cost estimates and block deployments before they execute. This prevents expensive mistakes from reaching production.
package terraform
# Block any deployment that increases monthly costs by more than $500
deny[msg] {
cost_increase := input.cost_estimate.monthly_cost_increase
cost_increase > 500
msg := sprintf(
"COST POLICY: This deployment increases monthly costs by $%.2f, which exceeds the $500 threshold. Contact your finance team for approval before deploying.",
[cost_increase]
)
}
package terraform
# Prevent use of oversized instances without approval
restricted_instance_types := {
"m5.24xlarge",
"r5.24xlarge",
"c5.24xlarge",
"x1.32xlarge",
"x1e.32xlarge",
"p3.16xlarge",
"p4d.24xlarge",
}
deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_instance"
instance_type := resource.change.after.instance_type
restricted_instance_types[instance_type]
msg := sprintf(
"COST POLICY: Instance type '%s' is restricted. Use a smaller instance type or request an exception via your ops lead.",
[instance_type]
)
}
package terraform
required_cost_tags := ["CostCenter", "Team", "Environment"]
deny[msg] {
resource := input.resource_changes[_]
resource.change.actions[_] == "create"
tag := required_cost_tags[_]
not resource.change.after.tags[tag]
msg := sprintf(
"COST POLICY: Resource %s is missing required tag '%s'. All new resources must have CostCenter, Team, and Environment tags for billing allocation.",
[resource.address, tag]
)
}
package terraform
warn[msg] {
resource := input.resource_changes[_]
resource.type == "aws_s3_bucket"
resource.change.actions[_] == "create"
# Check if there's a corresponding lifecycle config resource
lifecycle_resources := [r | r := input.resource_changes[_]; r.type == "aws_s3_bucket_lifecycle_configuration"]
count(lifecycle_resources) == 0
msg := sprintf(
"COST WARNING: S3 bucket %s has no lifecycle rule. Consider adding one to automatically transition or expire old objects.",
[resource.address]
)
}
If you've set up GitHub sync, cost estimates automatically appear as comments on pull requests:
aws_instance.app_server (t3.xlarge) +$121.47
aws_db_instance.postgres (db.r5.large) +$172.80
aws_nat_gateway.main +$32.40
aws_instance.bastion (t3.micro) +$8.47
This gives reviewers the cost impact before approving and merging the PR — no surprises on the next cloud bill.
After deploying infrastructure, use Discovery to find resources that are costing money but may no longer be needed:
Go to Discovery → New Scan and scan your cloud account.
After the scan, filter by resource type. Common cost culprits:
For resources you want to keep, import them into an IaC project. For orphaned resources, use ops0 to generate destroy Terraform that safely removes them.
For teams running Kubernetes clusters, ops0 provides per-namespace cost analysis via the ops0 agent:
Go to Kubernetes → Add Cluster and deploy the ops0 agent.
Navigate to Kubernetes → [your cluster] → Cost Analysis.
ops0 breaks down CPU, memory, GPU, and persistent volume costs per namespace. Sort by cost to identify which teams or applications are driving the most spend.
Compare requested resources vs actual usage. A pod requesting 4 CPU cores but using 0.2 is a candidate for right-sizing.
Kubernetes Cost Analysis — production cluster
─────────────────────────────────────────────────────────
Namespace CPU Memory PV Total/mo
─────────────────────────────────────────────────────────
ml-training $420 $180 $45 $645
api-services $210 $95 $20 $325
monitoring $85 $140 $60 $285
frontend $45 $30 $5 $80
dev-sandbox $290 $120 $0 $410 ← review
─────────────────────────────────────────────────────────
Total $1,050 $565 $130 $1,745
─────────────────────────────────────────────────────────
The dev-sandbox namespace in this example costs $410/month but has no persistent volumes — it's using a lot of CPU and memory that may not be needed outside business hours.
Use this checklist periodically to keep costs under control:
Write OPA/Rego policies that enforce cost controls automatically.
Schedule resource start/stop on a cron to cut non-production costs.
Per-namespace cost breakdown for your Kubernetes clusters.
Find orphaned and unmanaged resources wasting money.