How to create, version, share, and maintain custom images on Google Compute Engine for repeatable, production-grade VM deployments.


What Are Custom Images?

A custom image is a bootable copy of a disk that you own. It captures the OS, installed software, configuration files, and file system layout at a specific point in time. When you create a VM from a custom image, Compute Engine stamps that disk contents onto a fresh Persistent Disk and boots from it. After that, the VM and the image are independent.

Custom images are a global resource. They are not tied to a specific zone or region, though you can control where they are stored.

In practice: If you find yourself writing long startup scripts that install the same packages on every VM, you should probably build a custom image instead. Pre-bake the software into the image and skip the install step at boot time.


Custom Image vs Startup Script vs Machine Image

AspectCustom ImageStartup ScriptMachine Image
What it capturesSingle disk contentsCommands to run at bootAll disks + full VM configuration
Boot speedFast (everything pre-installed)Slow (installs on every boot)Fast
VM config included?No (disk only)NoYes (machine type, network, labels, metadata)
Failure riskLow (no runtime dependencies)High (network issues, package repo outages can break it)Low
CostStorage cost varies by location; common regions are about $0.050/GiB/month as of May 2026FreeStorage cost varies by location; common regions are about $0.050/GiB/month as of May 2026
Best forFleet deployments, autoscaling, golden imagesSimple setups, dev/test, one-off configurationVM cloning, backup/restore, migration

Pick a custom image when: You need many identical VMs that boot fast and start in a known state. This is the standard for production fleets.

Pick a startup script when: You are prototyping, running a single VM, or the setup is simple enough that boot-time installation is fine.

Pick a machine image when: You need to clone or back up an entire VM including its configuration and all attached disks.


When Custom Images Are Worth It

Custom images are standard practice in production environments where you run fleets of VMs. Here is when they make a real difference:

Autoscaling and Managed Instance Groups: When a MIG scales out, new VMs need to be ready fast. A custom image boots in seconds with everything pre-installed. A startup script that downloads and installs packages can add minutes to every scale-out event.

Security hardening: Organizations bake CIS benchmarks, endpoint security agents, audit logging daemons, and compliance configurations into an image. Every VM starts in a known-secure state rather than relying on a script to apply hardening correctly at boot.

Compliance requirements: Regulated industries (finance, healthcare, government) need auditable, repeatable infrastructure. A versioned custom image is a concrete artifact you can point to during an audit.

Complex software stacks: When your VMs need specific runtimes, database client libraries, proprietary tools, or application code baked in, a custom image avoids the complexity and fragility of installing everything at boot.

When you do not need them: Dev and test environments, quick prototypes, single VMs, or cases where a short startup script does the job. Custom images add operational overhead (build pipeline, versioning, patching) that is not worth it for small setups.


Creating a Custom Image

The Standard Workflow

  1. Launch a VM from a public base image
  2. SSH in and install your software, apply configurations
  3. Stop the VM (recommended for clean disk state)
  4. Create the image from the boot disk
# Step 1: Create a base VM
gcloud compute instances create image-builder \
  --machine-type=e2-medium \
  --image-family=debian-12 \
  --image-project=debian-cloud \
  --zone=us-central1-a
 
# Step 2: SSH in and install software
gcloud compute ssh image-builder --zone=us-central1-a
# ... install packages, configure, etc ...
 
# Step 3: Stop the VM for a clean image
gcloud compute instances stop image-builder --zone=us-central1-a
 
# Step 4: Create the image
gcloud compute images create my-app-v1 \
  --source-disk=image-builder \
  --source-disk-zone=us-central1-a \
  --family=my-app \
  --description="Base image with nginx and app v1"

Warning: You can use --force to create an image from a running VM, but this risks capturing an inconsistent disk state (partial writes, buffered data not flushed). Stop the VM first for reliable images. If you must image a running VM, at minimum flush buffers (sudo sync) and pause applications that are writing to disk.

Other Creation Methods

From a snapshot:

gcloud compute images create my-app-from-snapshot \
  --source-snapshot=my-disk-snapshot

From another image (copy across projects):

gcloud compute images create my-app-copy \
  --source-image=my-app-v1 \
  --source-image-project=platform-images

From a RAW image in Cloud Storage (for on-prem migrations):

gcloud compute images create imported-image \
  --source-uri=gs://my-bucket/disk.raw.tar.gz

Storage Location

By default, Compute Engine stores the image in the multi-region closest to the source. You can specify a location for compliance:

gcloud compute images create my-app-v1 \
  --source-disk=image-builder \
  --source-disk-zone=us-central1-a \
  --storage-location=eu

Use specific regions or multi-regions (us, eu, asia) based on where your VMs run and any data residency requirements.

Rate Limits

You can create one image per disk every 10 minutes, with a burst of up to 6 requests per 60 minutes. Plan your build pipeline around these limits.


Image Families

An image family is a named group of related images. The family always resolves to the most recent non-deprecated image in that group. Think of it as a latest pointer that automatically updates.

# Create an image and assign it to a family
gcloud compute images create my-app-v2 \
  --source-disk=image-builder \
  --source-disk-zone=us-central1-a \
  --family=my-app
 
# Create a VM using the family (always gets the latest version)
gcloud compute instances create web-server \
  --image-family=my-app \
  --image-project=my-project \
  --zone=us-central1-a

Key Point: Use image families when you want automation to follow the latest approved image in a release channel. Pin a specific image name when you need deterministic rollouts, exact rollback targets, or audit evidence that a VM came from one immutable image version.


Deprecation and Lifecycle

Compute Engine supports deprecation states to manage the lifecycle of custom images:

StateEffect
ACTIVEDefault. Image is fully usable.
DEPRECATEDStill usable, but the image family no longer resolves to it. A replacement image can be specified.
OBSOLETECannot be used to create new VMs. Use this when you want to block new launches before deleting the image.
DELETEDMarked for deletion. You cannot create new VMs from it.

Deprecate an image and point to its replacement:

gcloud compute images deprecate my-app-v1 \
  --state=DEPRECATED \
  --replacement=my-app-v2

Delete an old image (only after confirming nothing references it):

gcloud compute images delete my-app-v1

Tip: Deprecate old images but do not delete them immediately. Keep them around in case you need to roll back. Only delete after confirming no automation, templates, or running VMs still reference the old image.


Sharing Images Across Projects

The standard enterprise pattern is a dedicated image project — a single GCP project that stores approved golden images. Application teams in other projects reference images from that project.

Note: The examples below write service account addresses with [at] instead of the normal email separator to avoid site rendering issues. Use the normal separator when running the command.

Grant access to a specific image:

gcloud compute images add-iam-policy-binding my-app-v2 \
  --project=platform-images \
  --member='serviceAccount:123456-compute[at]developer.gserviceaccount.com' \
  --role=roles/compute.imageUser

Or grant project-level access (all images in the project):

gcloud projects add-iam-policy-binding platform-images \
  --member='serviceAccount:123456-compute[at]developer.gserviceaccount.com' \
  --role=roles/compute.imageUser

Consume the shared image:

gcloud compute instances create web-server \
  --image-family=my-app \
  --image-project=platform-images \
  --zone=us-central1-a

There are no network transfer fees for creating images or for creating disks from images, regardless of where the image is stored.


Golden Image Pipeline

In production, you typically automate the image build process rather than manually creating VMs and running commands. The workflow looks like this:

Build VM from public image
  → Install software and apply configuration
  → Generalize (remove instance-specific data, secrets, SSH keys)
  → Create image and assign to family
  → Test (launch a VM, verify it works)
  → Deprecate previous version
  → Update instance templates to use the new image family

Common automation tools:

ToolHow It Fits
Packer (HashiCorp)Build images declaratively. Define what to install in a template file, Packer creates the VM, runs your provisioners, and creates the image. Most popular option.
Cloud BuildGCP-native CI/CD. Trigger a build pipeline on code change that produces a new custom image.
TerraformCan create images, but Packer is better suited for the build step. Terraform is used to consume images (instance templates, VMs).

Versioning strategy: Use a consistent naming convention. Examples: my-app-v1, my-app-v2, or date-based my-app-20260518. Treat the image family as the moving channel and the image name as the immutable version. Use the family for rolling updates and the exact image name when a deployment must be reproducible.


Best Practices

PracticeWhy
Stop VMs before imagingAvoids inconsistent disk state from partial writes and buffered data
Never bake secrets into imagesEvery VM created from the image inherits its contents. Use Secret Manager or inject secrets at boot
Use image families intentionallyFamilies work well for rolling release channels. Pin exact image names for deterministic rollouts, audits, or controlled rollback.
Test every new imageLaunch a test VM from the image before updating production templates
Rebuild regularly for OS patchesBase images get security updates. Periodically rebuild from the latest public image, re-apply your configuration, and release a new version
Deprecate, do not immediately deleteKeep old images for rollback. Delete only after confirming nothing references them
Use guest OS featuresEnable GVNIC for high network bandwidth, UEFI_COMPATIBLE for Shielded VMs, SEV_SNP_CAPABLE for Confidential VMs
Use a dedicated image projectCentralize golden images in one project. Share via IAM. Avoids duplication across teams
Automate the build pipelineManual image creation does not scale. Use Packer or Cloud Build for repeatable, tested builds

TL;DR

  • Custom images capture a single disk’s contents (OS, software, config). Use them when you need fast, consistent VM deployments at scale.
  • Prefer custom images over startup scripts for production fleets. Faster boot, no runtime dependency on package repos, consistent state.
  • Assign images to a family for rolling release channels. Pin exact image names when reproducibility matters.
  • Manage lifecycle with deprecation states (DEPRECATED → OBSOLETE → DELETED). Keep old images for rollback.
  • Share images across projects with roles/compute.imageUser IAM bindings. Use a dedicated image project for golden images.
  • Automate builds with Packer or Cloud Build. Manual image creation does not scale past a handful of images.
  • As of May 2026, common-region storage is about $0.050/GiB/month; a 20 GiB image is roughly $1/month. Check the pricing page for your storage location.

Resources

Create Custom Images Official guide for creating images from disks, snapshots, and Cloud Storage.

Image Management Best Practices Google’s recommended patterns for building, versioning, and sharing images.

Image Families Best Practices How to use image families effectively for rolling updates.

Deprecate Custom Images Deprecation states and lifecycle management.

Share Images Across Projects IAM patterns for cross-project image sharing.

Disk and Image Pricing Storage costs for custom images and snapshots.

Machine Types and Images Overview of public images, image families, and the snapshots vs images distinction.

VM Startup Scripts When to use startup scripts instead of custom images.

Instance Templates How templates reference images for managed instance groups.