kubernetes/kops · error
cannot mix egress values in private or IPv6-capable subnets
Error message
cannot mix egress values in private or IPv6-capable subnets
What it means
During AWS network model building, kops groups NAT-capable subnets per zone and takes the Egress value from the first subnet as the zone-wide setting. If any other subnet in that zone declares a different egress value, the builder aborts because it cannot provision one egress path (NAT gateway, NAT instance, Transit Gateway, etc.) per zone with conflicting per-subnet settings. This is a cluster-spec validation error raised at `kops update cluster` model-render time, not an AWS API error.
Source
Thrown at pkg/model/awsmodel/network.go:432
egress := info.NATSubnets[0].Egress
publicIP := info.NATSubnets[0].PublicIP
allUnmanaged := true
for _, subnetSpec := range info.NATSubnets {
if !isUnmanaged(subnetSpec) {
allUnmanaged = false
}
}
if allUnmanaged {
klog.V(4).Infof("skipping network configuration in zone %s - all subnets unmanaged", zone)
continue
}
// Verify we don't have mixed values for egress/publicIP - the code doesn't handle it
for _, subnet := range info.NATSubnets {
if subnet.Egress != egress {
return fmt.Errorf("cannot mix egress values in private or IPv6-capable subnets")
}
if subnet.PublicIP != publicIP {
return fmt.Errorf("cannot mix publicIP values in private or IPv6-capable subnets")
}
}
var ngw *awstasks.NatGateway
var tgwID *string
var in *awstasks.Instance
if egress != "" {
if strings.HasPrefix(egress, "nat-") {
ngw = &awstasks.NatGateway{
Name: new(zone + "." + b.ClusterName()),
Lifecycle: b.Lifecycle,
Subnet: egressSubnet,
ID: new(egress),
AssociatedRouteTable: egressRouteTable,View on GitHub (pinned to 4c8573c808)
Solutions
- Open the cluster spec and make the `egress` field identical for every private/IPv6-capable subnet within the same zone (leave egress empty on all of them for kops-managed NAT gateways).
- If per-zone egress is genuinely needed, move the differing subnets into a different zone, since the check is per-zone (infoByZone).
- Run `kops edit cluster` (or `kops replace -f`) with the corrected spec, then `kops update cluster` again.
- If you intentionally want mixed egress, split the cluster or file a feature request — the builder explicitly does not support mixed values (see comment at network.go:429).
Example fix
# before (cluster.yaml, same zone) subnets: - name: us-east-1a-private zone: us-east-1a type: Private egress: nat-0abc123 - name: us-east-1a-private2 zone: us-east-1a type: Private egress: External # after subnets: - name: us-east-1a-private zone: us-east-1a type: Private egress: nat-0abc123 - name: us-east-1a-private2 zone: us-east-1a type: Private egress: nat-0abc123
Defensive patterns
Strategy: validation
Validate before calling
#!/bin/bash
# Run before `kops update cluster`: ensure all private subnets per zone share one egress value
kops get cluster -oyaml | awk '
/zone:/ {zone=$2} /egress:/ {print zone, $2}' | sort -u |
awk '{if (seen[$1] && seen[$1]!=$2) {print "CONFLICT in zone", $1; exit 1} seen[$1]=$2}' Type guard
func consistentEgress(subnets []kops.ClusterSubnetSpec, zone string) bool {
seen := map[string]bool{}
for _, s := range subnets {
if s.Zone != zone { continue }
seen[s.Egress] = true
if len(seen) > 1 { return false }
}
return true
} Try / catch
err := updateCluster(ctx)
if err != nil && strings.Contains(err.Error(), "cannot mix egress values") {
// normalize egress across subnets in the zone, then retry once
} Prevention
- Keep egress empty on all private subnets unless you deliberately reuse an existing NAT/TGW resource.
- Template subnet specs from a single variable so egress can never drift between blocks.
- Run `kops toolbox template`/dry-run and grep the rendered spec for per-zone egress consistency before applying.
- Review `kops get cluster -oyaml` after every manual edit to the subnets section.
When it happens
Trigger: Running `kops update cluster` when two or more subnets in the same zone that route through NAT (private or IPv6-capable subnets) specify different `egress` values in cluster.spec.networkID/subnets config — e.g. one subnet with egress: nat-xxxx and another with egress: External, or one with an eipalloc- ID and another empty.
Common situations: Hand-editing the cluster spec to give one private subnet a Transit Gateway egress while siblings still use the default NAT gateway; migrating a cluster gradually from NAT gateways to NAT instances and changing only some subnets; copy-pasting subnet blocks between zones/zones with differing egress fields.
Related errors
- cannot mix publicIP values in private or IPv6-capable subnet
- no networking mode set
- classic networking not supported
- subnet %q must specify a zone or the ID of an existing subne
- invalid AWS zone: %q in subnet %q
AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05).
Data as JSON: /api/errors/a608c9dac574f4b3.
Report an issue: GitHub.