ops0ops0

Cost Optimization

Use ops0 cost estimation, policies, Kubernetes cost analysis, and discovery to control cloud spending before and after deployments.


Before you start

At least one cloud integration connected
An IaC project with resources to manage (or plan to create one)

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  │
─────────────────────────────────────────────────────────
Estimates are based on on-demand pricing

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

IncludedNot included
EC2/Compute instance hoursData transfer costs
RDS/Cloud SQL/Azure SQLBandwidth charges
NAT Gateway hoursSupport plan charges
Load balancer hoursMarketplace software fees
Storage (S3/GCS/Azure Blob)Reserved/committed discounts
Kubernetes node costsSpot/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:

ops0 — Terraform Plan
Plan: 3 to add, 1 to change, 0 to destroy
💰 Cost impact: +$335.14/month
New monthly estimate: $1,850.97

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:

Right-size compute instances
Check CPU and memory utilization in your monitoring tool. If an EC2 instance is averaging 15% CPU, consider dropping from t3.xlarge to t3.medium — ~60% cost reduction.
Schedule non-production environments
Use ops0 Workflows to stop dev/staging EC2 instances and RDS clusters outside business hours (e.g., 6pm–8am). For a t3.large running 24/7, this saves ~65% of compute cost.
Add S3 lifecycle rules
Objects not accessed for 90 days can transition to S3 Glacier Instant Retrieval (68% cheaper than Standard). Objects not accessed for 180 days can go to Glacier Deep Archive (95% cheaper).
Delete orphaned resources
Run Discovery monthly and look for unattached EBS volumes ($0.10/GB/month), unused Elastic IPs ($0.005/hour each), and old snapshots. These accumulate silently.
Right-size Kubernetes pods
Use ops0 Kubernetes Cost Analysis to find namespaces where requested resources significantly exceed actual usage. Update resource requests/limits in your deployment manifests.

Next Steps