Migrate pd-ssd to pd-balanced and cut GCP disk costs ~40%

Your Google Cloud bill has an SSD line item paying for a performance ceiling you never reach. pd-ssd disks list at about $0.17 per GiB-month. pd-balanced disks give you the same SSD-class latency for about $0.10. That is roughly 40% off the same storage. The one wrinkle is that Google Cloud will not let you flip the type on a live disk the way AWS does, so the migration takes a little more care. Here is the whole thing.

Why pd-balanced is almost always cheaper than pd-ssd

pd-balanced is Google Cloud's modern general-purpose SSD disk. It is the default boot disk type for most machine families now. As of mid-2026, Google Cloud's Persistent Disk pricing lists pd-balanced at about $0.10 per GiB-month and pd-ssd at about $0.17 per GiB-month for the same storage in a US region like us-central1. Move a disk across and you bank roughly 40% before you touch anything else.

Both types sit on SSD, so latency stays in the same class. The thing you trade away is the performance ceiling, and it is a ceiling most disks never come near.

On Google Cloud's published Persistent Disk performance numbers, pd-ssd delivers 30 IOPS per GiB and pd-balanced delivers 6 IOPS per GiB. On throughput, pd-ssd offers about 0.48 MiB/s per GiB against pd-balanced's 0.28 MiB/s per GiB. So a large, busy disk can drive far more IO on pd-ssd than on pd-balanced of the same size.

Now look at what a normal disk actually does. At 6 IOPS per GiB, a 100 GiB volume on pd-balanced can reach 600 IOPS. A boot disk for an app server rarely sustains a fraction of that. For boot disks, log volumes, and most application storage, the pd-balanced ceiling sits far above the real workload, and the 40% is close to free.

The trap: disk type is immutable

Here is the part that catches everyone who comes over from AWS.

On AWS you migrate gp2 to gp3 with a single modify-volume call, and the disk converts underneath a running instance with no downtime. That gp2 to gp3 migration is a live change with the instance still serving traffic. Google Cloud works differently. A Persistent Disk's type is set when the disk is created and cannot be changed in place, so converting pd-ssd to pd-balanced means creating a new disk from a snapshot and swapping it in.

That swap is the whole job. You snapshot the pd-ssd disk, create a fresh pd-balanced disk from that snapshot, then detach the old disk and attach the new one. Both boot disks and data disks can only be swapped while the VM is stopped, so plan a short maintenance window per instance.

There is a second thing to watch during the swap. For a while you pay for three things at once: the original pd-ssd disk, the new pd-balanced disk, and the snapshot in between. The saving only lands once you have confirmed the new disk is healthy and deleted the original. Until then the meter runs a little higher, so avoid leaving a half-finished migration parked for a week.

Find your pd-ssd candidates by hand

Start by listing every pd-ssd disk in the project. The gcloud CLI filters on disk type directly.

gcloud compute disks list \
  --filter="type:pd-ssd" \
  --format="table(name, zone, sizeGb, users.basename())"

The users column tells you whether the disk is attached to an instance. A row with a user is a disk doing work and worth migrating. An empty users column means the disk is unattached, and an unattached disk is a different conversation. You are usually better off deleting it than converting it, because you are paying for storage that serves nothing.

Filter straight to the attached disks that are costing you:

gcloud compute disks list \
  --filter="type:pd-ssd AND users:*" \
  --format="table(name, zone, sizeGb, users.basename())"

That gives you the migration pile. One case deserves a second look before you move it: a small disk with heavy IO. pd-balanced scales its IOPS ceiling with size at 6 IOPS per GiB, so a 50 GiB volume caps around 300 IOPS. If a small database disk has been leaning on pd-ssd's higher ceiling, pull its real numbers before you touch it. The disk metrics compute.googleapis.com/instance/disk/read_ops_count and write_ops_count in Cloud Monitoring count operations per sample period, not IOPS, so align them to a per-second rate (the rate aligner) and read the peak. Compare that peak against 6 IOPS per GiB at its current size and decide.

Persistent Disks are zonal or regional, and gcloud compute disks list sweeps every zone in the project at once. Run it in every project you own, because a forgotten pd-ssd disk in a project nobody opens still bills every hour.

for proj in $(gcloud projects list --format="value(projectId)"); do
  echo "== $proj =="
  gcloud compute disks list --project="$proj" \
    --filter="type:pd-ssd AND users:*" \
    --format="table(name, zone, sizeGb)"
done

Migrate with a short maintenance window

Once you have the list, each disk follows the same three steps to build the replacement. Stop the instance so the disk can be swapped, snapshot the source, then create the pd-balanced disk from that snapshot.

# 1. Stop the instance so the disk can be detached.
gcloud compute instances stop my-instance --zone=us-central1-a

# 2. Snapshot the source pd-ssd disk.
gcloud compute disks snapshot my-disk \
  --zone=us-central1-a \
  --snapshot-names=my-disk-prebalanced-us-central1-a

# 3. Create a new pd-balanced disk from that snapshot.
gcloud compute disks create my-disk-balanced \
  --zone=us-central1-a \
  --type=pd-balanced \
  --source-snapshot=my-disk-prebalanced-us-central1-a

Snapshot names live in one project-wide namespace, not a per-zone one, so qualify the name with the disk and its zone when you migrate more than one. Two disks both called my-disk in different zones would otherwise collide on a bare my-disk-prebalanced and hand you the wrong rollback target.

Now swap the new disk in. A data disk detaches and reattaches by device name.

gcloud compute instances detach-disk my-instance \
  --disk=my-disk --zone=us-central1-a

gcloud compute instances attach-disk my-instance \
  --disk=my-disk-balanced --device-name=my-disk --zone=us-central1-a

A boot disk is the same swap with one extra flag. Add --boot on the attach so the instance knows which disk to boot from.

gcloud compute instances detach-disk my-instance \
  --disk=my-disk --zone=us-central1-a

gcloud compute instances attach-disk my-instance \
  --disk=my-disk-balanced --boot --zone=us-central1-a

Start the instance and confirm the workload is happy before you clean anything up.

gcloud compute instances start my-instance --zone=us-central1-a

If something looks wrong, rollback is quick, because the original pd-ssd disk is still sitting there untouched. Stop the VM, detach the pd-balanced disk, and reattach the original.

gcloud compute instances stop my-instance --zone=us-central1-a
gcloud compute instances detach-disk my-instance --disk=my-disk-balanced --zone=us-central1-a
# add --boot below for a boot disk; drop it for a data disk
gcloud compute instances attach-disk my-instance --disk=my-disk --boot --zone=us-central1-a
gcloud compute instances start my-instance --zone=us-central1-a

Only after the new disk has proven itself do you delete the original disk and the snapshot. This is the step that actually turns the swap into a lower bill.

gcloud compute disks delete my-disk --zone=us-central1-a --quiet
gcloud compute snapshots delete my-disk-prebalanced-us-central1-a --quiet

A quick decision tree

graph TD
  A[pd-ssd disk] --> B{Attached to an instance?}
  B -- No --> C[Delete it: you pay for storage serving nothing]
  B -- Yes --> D{Small disk with heavy IO?}
  D -- No --> E[Snapshot, recreate as pd-balanced, swap in a short window]
  D -- Yes --> F[Check Cloud Monitoring IOPS against 6 per GiB]
  F --> G{Real IOPS under the pd-balanced ceiling?}
  G -- Yes --> E
  G -- No --> H[Keep pd-ssd, or grow the disk to raise the pd-balanced ceiling]

How OhChimp approaches this

Doing this by hand works. The catch is that pd-ssd disks pile up across projects and zones over years, and the ones costing the most are the ones nobody remembers creating.

OhChimp scans your Google Cloud projects, finds the attached pd-ssd disks, and writes a reviewable plan for each one: snapshot, create the pd-balanced disk, swap it in, with the exact rollback if anything looks off. The IOPS question is answered per disk, so you read a recommendation instead of redoing the analysis. You click apply, nothing changes until you do, and the saving is then checked against your real Google Cloud bill.

See how the Google Cloud connection works on the Google Cloud integration page.