Skip to content

Reduce costs

Most teams can cut their GCP costs with a few changes to their nais.yaml manifests. The largest savings come from right-sizing database instances, adjusting resource requests, and tuning replica counts.

Find what to optimize

Before making changes, check your team's current resource usage and costs in Nais Console :

  • Cost — daily costs broken down by GCP service. Find it under your team's Cost tab.
  • Utilization — CPU and memory requested vs. actually used, per workload. Find it under your team's Utilization tab.

The Utilization page shows how much you request and how much you use. The gap between these numbers is what you're overpaying for.

Resource requests

Lower CPU requests

Kubernetes lets pods burst beyond their CPU request when the node has spare capacity. By removing the CPU limit and setting the request to the application's idle baseline, you pay for less without losing burst capacity.

Set the CPU request to the idle baseline (what your app uses during nights and weekends):

app.yaml
spec:
  resources:
    requests:
      cpu: 20m
      memory: 256Mi

Typical values:

Application type CPU request (prod) CPU request (dev)
Frontend 20m 10m
Backend (Ktor, Spring Boot) 30m 10m

Info

Don't set a CPU limit. Removing it lets the application burst freely when it needs more CPU, without paying for reserved capacity it rarely uses.

Warning

Don't go below your application's actual baseline. When nodes are busy, each pod only gets CPU proportional to its request. Too-low requests cause slow responses, failed health checks, and pod restarts. They also make the autoscaler flap, since even minor CPU usage exceeds the 50% scaling threshold.

Lower memory requests

Memory requests reserve capacity on the node, just like CPU. If your application requests 512 Mi but uses 200 Mi, the remaining 312 Mi is wasted — no other pod can use it.

Check actual memory usage in the Utilization tab in Nais Console , then set the request to match the observed peak with some headroom:

app.yaml
spec:
  resources:
    requests:
      cpu: 20m
      memory: 256Mi

Warning

If a pod exceeds its memory request and the node is under pressure, it may be evicted. Leave some margin above the observed peak.

Replicas

Reduce minimum replicas in dev

The default minimum is 2 replicas. Dev environments that don't need high availability can run with 1:

app.yaml
spec:
  replicas:
    min: 1
    max: 2

This halves the base cost for that workload in dev.

Lower maximum replicas

The default maximum is 4 replicas. If your application handles its load with fewer, lower the max to prevent unnecessary scaling:

app.yaml
spec:
  replicas:
    min: 2
    max: 3

See the automatic scaling reference for details on scaling thresholds and strategies.

Database tier

Database instances are often the largest line item on the Cost page.

Use a smaller tier in dev

Dev databases rarely need production-grade hardware. Switch to the smallest available tier:

app.yaml
spec:
  gcp:
    sqlInstances:
      - type: POSTGRES_17
        tier: db-f1-micro
        databases:
          - name: mydb

Warning

db-f1-micro does not have an SLA. This is fine for dev, but not for production.

Right-size your production database

Check your database's CPU usage in the Google Cloud Console or through database observability tools.

If CPU usage stays low, lower the tier. The smallest tier with an SLA is db-custom-1-3840:

app.yaml
spec:
  gcp:
    sqlInstances:
      - type: POSTGRES_17
        tier: db-custom-1-3840
        databases:
          - name: mydb

See the full list of available tiers.

Warning

Changing the tier restarts the database, causing a few minutes of downtime.

Fix slow queries instead of scaling up

High CPU usage often comes from missing indexes or expensive queries, not actual load. Before upgrading the tier, check for these using Query Insights in the Google Cloud Console.

Evaluate high availability settings

If your database has highAvailability: true, consider whether pointInTimeRecovery: true gives you sufficient protection instead. High availability runs a standby instance that doubles the instance cost .

app.yaml
spec:
  gcp:
    sqlInstances:
      - type: POSTGRES_17
        tier: db-custom-1-3840
        pointInTimeRecovery: true
        databases:
          - name: mydb

Database disk

Cloud SQL charges for allocated disk space , not used space.

Cap automatic disk growth

When diskAutoresize is enabled, GCP increases storage automatically when disk usage is high. The disk never shrinks back. Without a limit, this can grow unchecked.

Set diskAutoresizeLimit to cap the maximum size in GB:

app.yaml
spec:
  gcp:
    sqlInstances:
      - type: POSTGRES_17
        tier: db-custom-1-3840
        diskAutoresize: true
        diskAutoresizeLimit: 50
        databases:
          - name: mydb

The default limit is 0 (unlimited). Set it to a value that fits your expected data growth.

See the diskAutoresize and diskAutoresizeLimit reference.

Use HDD for low-I/O databases

The default disk type is SSD. If your database has low I/O requirements (infrequent reads and writes), HDD is cheaper:

app.yaml
spec:
  gcp:
    sqlInstances:
      - type: POSTGRES_17
        tier: db-custom-1-3840
        diskType: HDD
        databases:
          - name: mydb

Info

SSD is the right choice for most production databases. Use HDD only for archival or low-traffic databases.

Reclaim unused disk space

If your database has far more disk allocated than it actually uses, the only way to reclaim the space is a database migration. Cloud SQL does not support shrinking disk.

Advanced: database migration

These changes save the most but require a database migration.

Migrate to the Nais cluster

Databases outside the Nais cluster require a Cloud SQL Proxy sidecar, which adds overhead and cost. Migrating the database into the cluster removes this.

Tip

If you haven't migrated databases before, ask someone with experience first. Instances with non-default settings can be tricky to migrate.

Monitor your costs

Use the Cost and Utilization tabs in Nais Console to track the effect of your changes over time.