kubernetes/kops · error

instance group %q must specify exactly one zone

Error message

instance group %q must specify exactly one zone

What it means

Also in createInternalLB (pkg/model/gcemodel/api_loadbalancer.go:174): every instance group that runs the API server must specify exactly one zone for the GCE internal load balancer. When spec.zones is empty, kOps cannot determine which zone-level InstanceGroupManager to create and returns this error.

Source

Thrown at pkg/model/gcemodel/api_loadbalancer.go:174

		Lifecycle: b.Lifecycle,
	}
	c.AddTask(hc)

	// Collect ControlPlane and APIServer MIGs separately. The API backend service
	// includes both (both serve the kube-apiserver), while the kops-controller and
	// etcd backend services only include ControlPlane MIGs.
	var apiIGMs []*gcetasks.InstanceGroupManager
	var controlPlaneIGMs []*gcetasks.InstanceGroupManager // Currently these contain etcd instances
	requireEtcdLB := false
	for _, ig := range b.InstanceGroups {
		if !ig.RunsAPIServer() {
			continue
		}
		if len(ig.Spec.Zones) > 1 {
			return fmt.Errorf("instance group %q has %d zones, which is not yet supported for GCP", ig.GetName(), len(ig.Spec.Zones))
		}
		if len(ig.Spec.Zones) == 0 {
			return fmt.Errorf("instance group %q must specify exactly one zone", ig.GetName())
		}
		zone := ig.Spec.Zones[0]
		igm := &gcetasks.InstanceGroupManager{Name: s(gce.NameForInstanceGroupManager(b.Cluster.ObjectMeta.Name, ig.ObjectMeta.Name, zone)), Zone: s(zone)}
		apiIGMs = append(apiIGMs, igm)
		if ig.IsControlPlane() {
			controlPlaneIGMs = append(controlPlaneIGMs, igm)
		} else if ig.IsAPIServerOnly() {
			requireEtcdLB = b.Cluster.UsesNoneDNS()
		} else {
			return fmt.Errorf("instance group %q neither control-plane nor api-server", ig.GetName())
		}
	}
	backendService := &gcetasks.BackendService{
		Name:                  s(b.NameForBackendService("api")),
		Protocol:              s("TCP"),
		HealthChecks:          []*gcetasks.HealthCheck{hc},
		Lifecycle:             b.Lifecycle,
		LoadBalancingScheme:   s("INTERNAL"),

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Add exactly one zone to the instance group: run 'kops edit ig <name>' and set spec.zones: [<zone>]
  2. Ensure the zone matches one of the cluster's declared zones/subnets
  3. If you intended multi-zone, split into one IG per zone (see the multi-zone error)
  4. Re-run 'kops update cluster' and confirm the InstanceGroupManager task is generated

Example fix

// before
metadata:
  name: control-plane
spec:
  role: ControlPlane
  zones: []
// after
spec:
  role: ControlPlane
  zones: [us-central1-a]
Defensive patterns

Strategy: validation

Validate before calling

for _, ig := range instanceGroups {
	if ig.RunsAPIServer() && len(ig.Spec.Zones) != 1 {
		return fmt.Errorf("IG %q must specify exactly one zone for GCP internal LB", ig.Name)
	}
}

Type guard

func hasExactlyOneZone(ig *kops.InstanceGroup) bool {
	return ig.RunsAPIServer() && len(ig.Spec.Zones) == 1
}

Prevention

When it happens

Trigger: An API-serving instance group (RunsAPIServer() == true) whose spec.zones array is missing or zero-length while building a GCP cluster with an internal API load balancer (e.g. api.loadBalancer.type internal or UsesNoneDNS).

Common situations: Hand-written instance group manifests that omit zones (relying on cluster-level defaults); tooling that generates IGs without per-IG zones on GCP; IGs created before zones were made mandatory for this code path.

Related errors


AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05). Data as JSON: /api/errors/94d3204fa832ad87. Report an issue: GitHub.