Cost Optimization
Use ops0 cost estimation, policies, Kubernetes cost analysis, and discovery to control cloud spending before and after deployments.
Before you start
Step 1: Understand Cost Estimation in Plans
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.
What cost estimation covers
| 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 |
Step 2: Set a Budget Alert
Set a monthly budget for your organization so ops0 notifies you when spending approaches the limit:
Open Budget Settings
Go to Settings → Budget.
Set Monthly Limit
Enter your monthly budget in USD. ops0 tracks estimated spending across all projects.
Configure Alert Thresholds
Set alerts at 80% and 100% of budget. Notifications go to your configured Slack or email channels.
Review Monthly
Check the budget dashboard monthly to track trends and identify which projects are driving the most spend.
Step 3: Block Expensive Deployments with Policies
Policies can inspect cost estimates and block deployments before they execute. This prevents expensive mistakes from reaching production.
Block deployments over a cost threshold
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]
)
}
Restrict expensive instance types
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]
)
}
Require cost allocation tags on new resources
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]
)
}
Warn on storage without lifecycle rules
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]
)
}
Step 4: Cost Estimation in GitOps PR Comments
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.
Step 5: Identify Expensive Resources with Discovery
After deploying infrastructure, use Discovery to find resources that are costing money but may no longer be needed:
Run a Discovery scan
Go to Discovery → New Scan and scan your cloud account.
Filter for expensive resource types
After the scan, filter by resource type. Common cost culprits:
- Unattached EBS volumes (paying for storage with no instance using it)
- Unused Elastic IPs (billed whether attached or not)
- Idle NAT Gateways (charged per hour)
- Stopped EC2 instances (EBS storage and Elastic IPs still billed)
- Unused RDS snapshots (storage costs accumulate)
Import and manage or delete
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.
Step 6: Kubernetes Cost Analysis
For teams running Kubernetes clusters, ops0 provides per-namespace cost analysis via the Hive Agent:
Connect your cluster
Go to Kubernetes → Add Cluster and deploy the Hive Agent.
Open Cost Analysis
Navigate to Kubernetes → [your cluster] → Cost Analysis.
Review per-namespace breakdown
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.
Identify over-provisioned workloads
Compare requested resources vs actual usage. A pod requesting 4 CPU cores but using 0.2 is a candidate for right-sizing.
Example cost breakdown
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.
Cost Optimization Checklist
Use this checklist periodically to keep costs under control:
Next Steps
Creating Policies
Write OPA/Rego policies that enforce cost controls automatically.
Workflows
Schedule resource start/stop on a cron to cut non-production costs.
Kubernetes Cost Analysis
Per-namespace cost breakdown for your Kubernetes clusters.
Discovery
Find orphaned and unmanaged resources wasting money.