GCP ACE: Managed Instance Groups and Health Checks
A Managed Instance Group (MIG) is how Compute Engine runs a fleet of identical VMs as a single resource: autoscaling, autohealing, rolling updates and multi-zone distribution all come from the group, not from any individual VM. MIGs are also the standard backend for a load balancer, but that part of the picture isn’t covered here — this post is about the group itself, its templates, and the health checks that keep it honest.
Instance templates
Every MIG is built from an instance template: a reusable definition of machine type, boot disk, network interfaces, metadata, labels, service account and scopes, GPUs, and Shielded VM settings. Templates are immutable — you can’t edit one after creation. To change anything, you create a new template version and point the MIG at it; the old template stays around until nothing references it.
gcloud compute instance-templates create web-template-v2 \
--machine-type=e2-medium \
--image-family=debian-12 \
--image-project=debian-cloud \
--network=default \
--tags=allow-health-check \
--metadata=startup-script='#! /bin/bash
apt-get update && apt-get install -y nginx'
Templates are global by default, usable from any region. A regional instance template exists too, and you need it when the template references something region-scoped — a reserved internal IP, a regional persistent disk, or a specific subnet that only exists in one region.
Creating the group
MIGs come in two shapes:
- Zonal MIG: all instances in one zone. Simpler, lower latency between instances, no cross-zone data transfer.
- Regional MIG: instances spread across the zones of a region. The default and recommended choice for anything that needs high availability — if a zone goes down, the group keeps serving from the others.
gcloud compute instance-groups managed create web-mig \
--template=web-template-v2 \
--size=3 \
--region=europe-southwest1
Size limits that show up on the exam: a zonal MIG supports up to 1,000 instances; a regional MIG supports up to 2,000 instances. Both are soft limits — Cloud Support can raise them on request, but don’t expect the exam to test the exception, just the default ceiling.
Target distribution shape (regional MIGs)
A regional MIG’s --target-distribution-shape decides how it spreads instances across zones:
| Shape | Behavior |
|---|---|
EVEN (default) | Keeps instance counts within 1 of each other across zones. Required for sole-tenant VM groups. |
BALANCED | Prioritizes getting capacity where it’s available, while still spreading across zones to limit the blast radius of a zonal failure. |
ANY | Picks zones purely to maximize use of available resources/reservations — no HA guarantee. Good for batch workloads. |
ANY_SINGLE_ZONE | Puts the whole group in one zone, chosen for resource availability. For batch jobs with heavy inter-instance communication. |
BALANCED and ANY_SINGLE_ZONE require disabling proactive instance redistribution.
Autoscaling
The autoscaler adds or removes instances between --min-num-replicas and --max-num-replicas based on one or more signals: CPU utilization, HTTP load balancing serving capacity, a Cloud Monitoring metric, or a schedule.
gcloud compute instance-groups managed set-autoscaling web-mig \
--region=europe-southwest1 \
--min-num-replicas=2 \
--max-num-replicas=10 \
--target-cpu-utilization=0.6 \
--cool-down-period=90
--cool-down-period is the time the autoscaler gives a new instance to initialize before counting its metrics — default 60 seconds. Set it longer if your app has a slow startup (JVM warm-up, cache priming, etc.), or the autoscaler will judge a still-booting instance as underutilized and scale down prematurely.
--mode controls what the autoscaler is allowed to do:
| Mode | Behavior |
|---|---|
ON (default) | Scales out and in according to the policy. |
ONLY_SCALE_OUT | Adds instances when load rises but never shrinks the group. Useful to protect capacity, for example during a rollout or while investigating VMs. |
OFF | Temporarily disables autoscaling. The configuration is kept, so you can turn it back on later. |
In every mode the autoscaler keeps calculating a recommended group size, so you can see what it would do even when it’s off. Separately, --scale-in-control caps how fast the group shrinks, which helps avoid flapping.
Autohealing
Autohealing is what makes a MIG self-repairing at the application level, as opposed to only the infrastructure level. Compute Engine already restarts a VM that stops running; autohealing goes further and replaces a VM whose app has stopped responding correctly, even though the VM itself looks fine.
gcloud compute instance-groups managed update web-mig \
--region=europe-southwest1 \
--health-check=web-autohealing-check \
--initial-delay=300
--initial-delay is the grace period (in seconds) the MIG waits after an instance is created before it starts counting health check failures against it — this stops a VM from being killed mid-boot. There’s no fixed exam-tested default; the practical pattern is to set it a bit longer than your app’s real startup time (300s is the commonly used example in Google’s own docs).
If a VM fails the attached health check for the configured unhealthy threshold, the MIG deletes it and creates a replacement from the current instance template — same as a normal scale-up, not a reboot.
Health checks
This is the part that trips people up on the exam: the same health check resource can be used two different ways, and the two uses answer different questions.
- Autohealing health check (attached to the MIG): “is this instance healthy enough to keep running?” A failure → the instance gets recreated.
- Load balancer health check (attached to the backend service): “should this instance receive traffic right now?” A failure → the instance is taken out of rotation, but it is not recreated.
You can point both uses at the same health check object, or use two separate ones with different thresholds — e.g., a lenient LB health check that pulls an instance out quickly, and a stricter autohealing check that only replaces it after a longer, more certain failure pattern, so you don’t churn VMs over a brief blip.
Creating a health check
gcloud compute health-checks create http web-health-check \
--port=80 \
--request-path=/healthz \
--check-interval=10s \
--timeout=5s \
--healthy-threshold=2 \
--unhealthy-threshold=3
Defaults if you don’t override them: --check-interval=5s, --timeout=5s, --healthy-threshold=2, --unhealthy-threshold=2. healthy-threshold is the number of consecutive successes needed to mark an unhealthy instance healthy again; unhealthy-threshold is consecutive failures before it’s marked unhealthy.
Supported protocols: HTTP, HTTPS, HTTP/2, TCP, SSL (TLS), and gRPC. Pick the protocol that matches what the backend actually serves — an HTTPS backend behind an HTTP health check will often fail in ways that are confusing to debug.
Legacy vs modern health checks
Older http-health-checks / https-health-checks resources (no health-checks in the command) still exist and are what some regional external passthrough Network Load Balancers require, since they only support legacy checks via target pools. Everywhere else — HTTP(S), TCP, SSL, gRPC, all the current load balancer types, and autohealing — use the modern, unified health-checks resource. Default to the modern one unless something specifically forces you into target pools.
The firewall rule nobody remembers
Health check probes come from Google’s own IP ranges, not from the backend’s network, so they need an explicit ingress firewall rule or they’ll be silently dropped — which looks exactly like “my app is down” in the backend service’s health state.
gcloud compute firewall-rules create fw-allow-health-check \
--network=default \
--direction=INGRESS \
--action=ALLOW \
--rules=tcp:80 \
--source-ranges=130.211.0.0/22,35.191.0.0/16 \
--target-tags=allow-health-check
130.211.0.0/22 and 35.191.0.0/16 are the standard ranges for proxy-based and most modern health checks. This is one of the highest-yield things to just memorize for the exam — “instances are healthy per the app but the LB/MIG shows them unhealthy” is almost always this firewall rule missing.
Updating a MIG
Changing the instance template doesn’t touch running instances by itself — you have to trigger a rollout.
gcloud compute instance-groups managed rolling-action start-update web-mig \
--region=europe-southwest1 \
--version=template=web-template-v2 \
--max-surge=2 \
--max-unavailable=0
--max-surge: how many extra instances beyond the target size the group may create while updating (spin up new, then retire old — zero downtime capacity-wise).--max-unavailable: how many existing instances may be down at once during the update.--type(proactiveby default, oropportunistic): proactive actively replaces instances right away; opportunistic only replaces them as they’d naturally be recreated anyway (scale events, autohealing), which is slower but less disruptive.--minimal-action/--most-disruptive-allowed-action: whether an update can get away with aRESTART, needs aREPLACE, or whatever is least invasive for the specific template change (e.g., a metadata-only change doesn’t need a full replace).
You can also run a canary update: roll the new template out to a fixed number or percentage of instances first with --canary-version, watch it, then promote.
Instance redistribution (regional MIGs)
A regional MIG can proactively move instances between zones to keep the target distribution shape balanced — e.g., after you add a new zone, or after a zone outage ends and the group rebalances back. --instance-redistribution-type=PROACTIVE (default) does this automatically; NONE leaves existing instances where they are and only affects placement of new ones.
Stateful MIGs
By default a MIG is stateless: any instance can be deleted and replaced, and nothing instance-specific survives. A stateful MIG preserves per-instance state across recreation — attached non-boot persistent disks, instance name, and/or specific metadata — for workloads like a cluster of brokers that each need their own data and identity. You configure this per-instance or at the MIG level with --stateful-disk. It’s less common on the exam than the stateless path, but you should recognize “preserve instance names/disks across restarts” as the cue for it.
Standalone instance groups vs MIGs
An unmanaged / standalone instance group is just a flat list of existing VMs grouped together — useful mainly for load-balancing a set of heterogeneous legacy VMs that weren’t built from a common template. It gets none of the MIG machinery: no autoscaling, no autohealing, no rolling updates, no instance templates. If a question mentions any of those features, the answer is a MIG; if it explicitly says the VMs are different configurations that already exist and just need to sit behind a load balancer, that’s the standalone case.
Named ports
A MIG exposes named ports — a label-to-port mapping (e.g., http:80) that a backend service references instead of a raw port number. This is what lets a load balancer’s backend service know which port on each instance to actually send traffic to, independent of the instance’s own configuration.
gcloud compute instance-groups managed set-named-ports web-mig \
--region=europe-southwest1 \
--named-ports=http:80,https:443
Exam Takeaways
- Instance templates are immutable; updating a MIG means pointing it at a new template version, not editing the old one.
- Zonal MIG: up to 1,000 instances. Regional MIG: up to 2,000 instances, spread per the
--target-distribution-shape(EVENdefault). - Autoscaler
--cool-down-perioddefaults to 60s — bump it for slow-starting apps to avoid premature scale-down. - Autohealing and load balancer health checks are two separate uses of the same health check resource type: autohealing recreates the instance, the LB just stops routing to it. They can share a health check or use different ones.
- Health check defaults: 5s interval, 5s timeout, healthy/unhealthy threshold of 2. Supports HTTP, HTTPS, HTTP/2, TCP, SSL, gRPC.
- The firewall rule allowing
130.211.0.0/22and35.191.0.0/16on the health check port is mandatory and is the most common cause of “healthy app, unhealthy backend.” - Rolling updates are controlled with
--max-surge/--max-unavailableand--type(proactivevsopportunistic); canary rollouts use--canary-version. - Stateful MIGs preserve per-instance disks/names/metadata across recreation; stateless (default) does not.
- Standalone/unmanaged instance groups have none of the MIG automation — no autoscaling, autohealing, or rolling updates.
- Named ports on a MIG are what a load balancer’s backend service uses to find the right port on each instance.
Sources
- ↗ Google Cloud: Managed instance groups overview
- ↗ Google Cloud: Instance templates
- ↗ Google Cloud: Autoscaling groups of instances
- ↗ Google Cloud: Turn off or restrict an autoscaler
- ↗ Google Cloud: Configure an autohealing policy in a MIG
- ↗ Google Cloud: Use health checks
- ↗ Google Cloud: Health check concepts
- ↗ Google Cloud: Firewall rules for load balancers
- ↗ Google Cloud: Updating instances in a MIG
- ↗ Google Cloud: Set a target distribution shape for a regional MIG
- ↗ Google Cloud: Stateful MIGs overview