Google Cloud persistent disks grow online. The disk hardware is resized at the GCP control-plane layer in seconds, then the partition table and filesystem are stretched live from inside the VM. No detach, no reboot, no maintenance window. The whole operation is three commands once you know the device path: gcloud compute disks resize to enlarge the disk, sudo growpart to expand the partition, and sudo resize2fs (ext4) or sudo xfs_growfs (XFS) to stretch the filesystem. Below is the exact procedure, the verification commands to run before and after, the caveats that bite people once (shrinking, MBR ceiling, Extreme PD throttling), and a Console vs gcloud vs Terraform comparison so you can pick the right interface for your workflow.
How do I increase a Google Cloud VM disk size without rebooting?
Run gcloud compute disks resize DISK_NAME --size NEW_SIZE --zone ZONE to enlarge the persistent disk at the GCP layer. The hypervisor exposes the new size to the running VM within a few seconds. SSH into the instance and confirm the kernel sees it with lsblk. Expand the partition with sudo growpart /dev/DEVICE PART_NUM (typically /dev/sda 1 for boot disks on most public images). Finally, stretch the filesystem: sudo resize2fs /dev/sda1 for ext4 or sudo xfs_growfs / for XFS. Verify with df -h. The entire flow runs against a live VM with active connections, services, and disk I/O. No detach, no reboot, no downtime. For Extreme PD the only constraint is one resize per six hours, and shrinking is never supported on any disk type.
Jump to:
- Before you start: prerequisites and backups
- Step 1: Resize the disk in GCP
- Step 2: Expand the partition
- Step 3: Resize the filesystem
- Verify the new size
- Detect the filesystem before you resize it
- Console vs gcloud vs Terraform
- Caveats and gotchas
- What to do next
- FAQ
Before you start: prerequisites and backups
You need the Compute Instance Admin (v1) IAM role (roles/compute.instanceAdmin.v1) or an equivalent custom role with compute.disks.update. The VM can be running. The disk does not need to be detached.
Take a snapshot first. Snapshots are incremental, cheap, and the only single-command rollback you have if the partition table goes sideways:
gcloud compute snapshots create pre-resize-$(date +%Y%m%d-%H%M) \
--source-disk :disk_name \
--source-disk-zone :zoneA 100 GB disk snapshot completes in seconds and costs cents. Mistakes on a live disk are not cheap.
Capture the current state so you have a before-and-after to compare:
df -h
lsblkdf -h shows mounted filesystems and their used/free space. lsblk shows the block-device tree (disk → partitions → filesystems) and is where you'll confirm the new size lands.
Step 1: Resize the disk in GCP
The gcloud one-liner:
gcloud compute disks resize :disk_name \
--size :new_size \
--zone :zone--size is in GiB. The disk name is the persistent disk's name, not the VM's (though for boot disks they usually match). The flag is --zone for zonal disks; use --region instead for regional (replicated) disks.
For boot disks the disk name usually equals the VM name. List disks to confirm:
gcloud compute disks list --filter="zone::zone"The resize takes a few seconds and returns immediately. The new size is visible from inside the VM the moment the gcloud command returns, but the kernel may need a nudge to re-read the block-device geometry. Run lsblk from inside the VM to confirm. If the disk still shows the old size, force a rescan:
echo 1 | sudo tee /sys/class/block/:device/device/rescanThis is rare on modern public images (Debian, Ubuntu, RHEL, Rocky all auto-detect) but worth knowing.
Console method: Compute Engine > Disks > click the disk > Edit > change Size > Save. Same effect, just slower if you're resizing more than one disk.
Step 2: Expand the partition
The kernel now sees the larger disk, but the partition table still says "this partition ends at the old boundary." Fix that with growpart from the cloud-utils-growpart package (pre-installed on every GCP public image):
sudo growpart /dev/:device :partitionTwo arguments, space-separated: the device (/dev/sda) and the partition number (1). Note the space, not a colon and not concatenated. A common typo is growpart /dev/sda1 (no space) which fails because growpart needs the disk and the partition index separately.
Output looks like:
CHANGED: partition=1 start=2048 old: size=209713119 end=209715167 new: size=419430399 end=419432447
If growpart isn't installed (rare, but happens on minimal or custom images):
# Debian / Ubuntu
sudo apt-get update && sudo apt-get install -y cloud-guest-utils
# RHEL / Rocky / CentOS
sudo dnf install -y cloud-utils-growpartFor data disks with a single partition the call is identical with a different device, for example sudo growpart /dev/sdb 1. For data disks formatted as a whole-disk filesystem (no partition table) skip this step and go straight to filesystem resize against the raw device.
Step 3: Resize the filesystem
The partition is now the full size of the disk, but the filesystem inside the partition still believes it's the old size. The command depends on the filesystem type.
ext4 (Debian, Ubuntu, most custom Linux images):
sudo resize2fs /dev/:device:partitionresize2fs reads the partition geometry and grows the ext4 superblock to match. Online resize on a mounted filesystem is supported and is what runs here. Expected output:
resize2fs 1.46.5 (30-Dec-2021)
Filesystem at /dev/sda1 is mounted on /; on-line resizing required
old_desc_blocks = 13, new_desc_blocks = 25
The filesystem on /dev/sda1 is now 52428795 (4k) blocks long.
XFS (RHEL 8+, Rocky, AlmaLinux, some Ubuntu cloud images):
sudo xfs_growfs :mountXFS resizes by mount point, not by device. / is the root mount; for a data disk mounted at /data it would be sudo xfs_growfs /data. XFS can only grow, never shrink, which matches the GCP disk constraint anyway.
Btrfs (uncommon on GCP, but supported):
sudo btrfs filesystem resize max :mountIf you don't know the filesystem type, the next section has the detection commands.
Verify the new size
df -h
lsblkdf -h should now show the new free space on the mount point. lsblk should show the partition matching the disk size.
A complete before-and-after on a boot disk grown from 100 GB to 200 GB:
# Before
$ df -h /
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 98G 42G 52G 45% /
# After
$ df -h /
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 196G 42G 149G 22% /
Same Used, larger Size. That's the green light. Total wall-clock for the three commands on a typical boot disk: under 30 seconds, no reboot, active SSH session never drops.
Detect the filesystem before you resize it
Run any of these to identify the filesystem type and partition layout. Useful when you've inherited a VM and don't know what shape its disk is in.
# Filesystem type per partition
lsblk -f
# Filesystem type of the root mount
findmnt -T / -o SOURCE,TARGET,FSTYPE
# Disk and partition geometry
sudo parted -l
# Quick filesystem type lookup by device
sudo blkid /dev/sda1lsblk -f is the fastest single command and shows everything at once: device tree, filesystem type, label, UUID, and mountpoint.
Cheat sheet for picking the resize command:
| Filesystem | Resize command |
|---|---|
| ext2 / ext3 / ext4 | sudo resize2fs /dev/DEVICE |
| XFS | sudo xfs_growfs MOUNT_POINT |
| Btrfs | sudo btrfs filesystem resize max MOUNT_POINT |
| ZFS | sudo zpool online -e POOL DEVICE |
| FAT32 / NTFS | not online-resizable on Linux |
The first three cover ~99% of GCP Linux VMs.
Console vs gcloud vs Terraform
The resize itself is identical at the API layer. The interface choice is about your workflow.
| Approach | Best for | Command / action |
|---|---|---|
| Cloud Console | One-off resizes, exploring | Compute Engine > Disks > Edit > Size |
gcloud CLI | Scripted, one-shot, ad-hoc batches | gcloud compute disks resize DISK --size N --zone Z |
| Terraform | Infra-as-code, audit trail, multi-environment | google_compute_disk.size = 200 then terraform apply |
| REST API | Programmatic, custom tooling | PATCH /compute/v1/projects/PROJECT/zones/ZONE/disks/DISK |
For Terraform specifically, the only attribute that changes is size:
resource "google_compute_disk" "data" {
name = "data-disk"
zone = "us-central1-a"
size = 200 # was 100
type = "pd-balanced"
}
Terraform performs the resize in place. It does NOT trigger a replacement, which is exactly what you want. The growpart and resize2fs steps still have to happen inside the VM, typically via a remote-exec provisioner or a separate Ansible/SSM run.
To batch-resize many disks via gcloud, a Bash for loop over a disk-name list is the simplest pattern:
for disk in web-01 web-02 web-03; do
gcloud compute disks resize "$disk" --size 200 --zone us-central1-a --quiet
donePair with a Bash while loop and a gcloud compute disks describe poll if you need to wait for the resize to be observed by each VM before proceeding to the partition step.
Caveats and gotchas
You cannot shrink a persistent disk. Ever. The only path to a smaller disk is: create a new smaller disk, copy data over, switch the VM to use it, delete the old one. Plan capacity with this in mind; over-provisioning is reversible only the hard way.
MBR partition tables cap at 2 TB. GPT supports the full range up to 64 TiB. GCP public images ship GPT by default, but custom images imported from older infrastructure may still be MBR. Check with sudo parted /dev/sda print | grep "Partition Table". If it says msdos, you're on MBR and capped at 2 TB.
Maximum disk size is 64 TiB for Balanced, Performance (SSD), Standard, and Extreme persistent disks. Hyperdisk goes higher but uses a different resize path.
Extreme PD throttles resizes to once per 6 hours. Plan accordingly. The other disk types have no such limit.
Custom images may not auto-detect the new size on boot. Public Google-managed images (Debian, Ubuntu, RHEL, Rocky, Windows) auto-grow the boot disk's partition and filesystem on first boot after a resize. Custom or imported images do not. If you're on a custom image, the three-step procedure above is mandatory.
Boot disks vs data disks. The device is /dev/sda for the boot disk on most public images and /dev/sdb, /dev/sdc, etc. for additional data disks. NVMe-backed instances (C3, N4, third-gen and later) use /dev/nvme0n1, /dev/nvme0n2 instead. lsblk is the source of truth.
Snapshots before, not regrets after. A snapshot is faster than typing this sentence and saves the disk in its current state. Cost is incremental-only, measured in cents. There is no excuse to skip it on production disks.
Performance scales with capacity on some disk types. Standard PD throughput is capped at 1.2 MB/s per GB of provisioned size. Going from 100 GB to 200 GB doubles your throughput ceiling. Balanced PD and Performance PD have different scaling rules. Check the GCP disk performance docs if you care about the exact numbers for your disk type.
Don't run fdisk just because an old guide tells you to. Some older blog posts (including the one currently ranking on dev.to) advise running sudo fdisk /dev/sda and pressing w to "fix the GPT partition table" before growpart. This is unnecessary on modern GCP public images and risks corruption if you press the wrong key in fdisk's interactive mode. growpart handles the partition table update on its own.
What to do next
- The canonical reference: Google Cloud's official Resize a persistent disk guide covers the gcloud and Console flows plus Windows-specific steps.
- How to Export All MySQL Databases: the "snapshot before resize" hygiene applies double when a database lives on the disk. Run a logical dump first.
- How to ZIP Multiple Directories into Individual Files: useful for archiving the freed-up space you'll have after a resize.
- How to Export and Import PuTTY Settings: if you're SSH-ing from Windows into a GCP VM, save your session config before swapping machines.
- Bash For Loops and Bash While Loops: the patterns I use to batch-resize a fleet of disks and poll for completion.
- The
growpartsource lives in the cloud-utils repo on GitHub if you want the implementation details.





